Compare commits

..

No commits in common. "main" and "v7.9.0" have entirely different histories.
main ... v7.9.0

206 changed files with 8823 additions and 11320 deletions

View File

@ -77,14 +77,6 @@ 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%;
@ -393,12 +385,6 @@ 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,

View File

@ -34,10 +34,6 @@
margin-bottom: 20px;
}
.woocommerce-cart-form__cart-item .wcsg_add_recipient_fields_container label {
margin-bottom: 4px;
}
.wcsg_add_recipient_fields_container .wcsg_add_recipient_fields .woocommerce_subscriptions_gifting_recipient_email {
padding: 0;
margin-bottom: 20px;

View File

@ -3,10 +3,10 @@ body.wcs-modal-open {
}
.wcs-modal {
display: none;
position: fixed;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
height: 0;
@ -17,7 +17,6 @@ body.wcs-modal-open {
}
.wcs-modal.open {
display: flex;
position: fixed;
width: 100%;
height: 100vh;
@ -51,10 +50,6 @@ body.wcs-modal-open {
top: 0;
right: 0;
z-index: 50;
border: none;
background: transparent;
padding: 0;
cursor: pointer;
}
.wcs-modal .content-wrapper .modal-header {

View File

@ -15,17 +15,6 @@
margin-bottom: 0.5em;
}
button.subscription-auto-renew-toggle,
button.subscription-auto-renew-toggle:hover {
background: none;
border: none;
padding: 0;
cursor: pointer;
font: inherit;
box-shadow: none;
text-shadow: none;
}
.subscription-auto-renew-toggle {
margin-left: 5px;
margin-bottom: 2px;

View File

@ -639,53 +639,6 @@ 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>'
);
@ -700,9 +653,9 @@ jQuery( function ( $ ) {
$( '.options_group.subscription_pricing' )
);
// 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.
// 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.
if (
$( '#variable_product_options .variable_subscription_pricing' ).length >
0
@ -715,7 +668,6 @@ jQuery( function ( $ ) {
) {
$.moveSubscriptionGiftingFields();
}
// When a variation is added
$( '#woocommerce-product-data' ).on(
'woocommerce_variations_added woocommerce_variations_loaded',
@ -725,7 +677,6 @@ jQuery( function ( $ ) {
$.showHideVariableSubscriptionMeta();
$.showHideSyncOptions();
$.setSubscriptionLengths();
initLinkedDownloadableProductFieldsForVariationProduct();
}
);
@ -739,7 +690,6 @@ jQuery( function ( $ ) {
$.showHideSyncOptions();
$.disableEnableOneTimeShipping();
$.showHideSubscriptionsPanels();
relocateLinkedDownloadableProductFields();
}
// Update subscription ranges when subscription period or interval is changed

View File

@ -11,11 +11,8 @@ jQuery( function ( $ ) {
var $early_renewal_modal_content = $( '.wcs-modal > .content-wrapper' );
function getTxtColor() {
if ( ! txtColor ) {
// Create a temporary link to get the theme's accent color.
var $tempLink = $( '<a href="#" style="display:none;"></a>' ).appendTo( $toggleContainer );
txtColor = getComputedStyle( $tempLink[ 0 ] ).color;
$tempLink.remove();
if ( ! txtColor && $icon && $icon.length ) {
txtColor = getComputedStyle( $icon[ 0 ] ).color;
}
return txtColor;
@ -41,6 +38,9 @@ jQuery( function ( $ ) {
function onToggle( e ) {
e.preventDefault();
// Remove focus from the toggle element.
$toggle.trigger( 'blur' );
// Ignore the request if the toggle is disabled.
if ( $toggle.hasClass( 'subscription-auto-renew-toggle--disabled' ) ) {
return;
@ -105,20 +105,17 @@ jQuery( function ( $ ) {
$icon.removeClass( 'fa-toggle-off' ).addClass( 'fa-toggle-on' );
$toggle
.removeClass( 'subscription-auto-renew-toggle--off' )
.addClass( 'subscription-auto-renew-toggle--on' )
.attr( 'aria-checked', 'true' );
.addClass( 'subscription-auto-renew-toggle--on' );
}
function displayToggleOff() {
$icon.removeClass( 'fa-toggle-on' ).addClass( 'fa-toggle-off' );
$toggle
.removeClass( 'subscription-auto-renew-toggle--on' )
.addClass( 'subscription-auto-renew-toggle--off' )
.attr( 'aria-checked', 'false' );
.addClass( 'subscription-auto-renew-toggle--off' );
}
function blockToggle() {
$toggle.addClass( 'subscription-auto-renew-toggle--disabled' );
$toggleContainer.block( {
message: null,
overlayCSS: { opacity: 0.0 },
@ -126,7 +123,6 @@ jQuery( function ( $ ) {
}
function unblockToggle() {
$toggle.removeClass( 'subscription-auto-renew-toggle--disabled' );
$toggleContainer.unblock();
}

View File

@ -18,8 +18,7 @@ jQuery( document ).ready( function ( $ ) {
.find( '.recipient_email' )
.trigger( 'focus' );
}
} )
.removeClass( 'hidden' );
} );
const shipToDifferentAddressCheckbox = $( document ).find(
'#ship-to-different-address-checkbox'
@ -32,14 +31,12 @@ jQuery( document ).ready( function ( $ ) {
$( this )
.closest( '.wcsg_add_recipient_fields_container' )
.find( '.wcsg_add_recipient_fields' )
.slideUp( 250 )
.addClass( 'hidden' );
.slideUp( 250 );
const recipientEmailElement = $( this )
.closest( '.wcsg_add_recipient_fields_container' )
.find( '.recipient_email' );
recipientEmailElement.val( '' );
hideValidationErrorForEmailField( recipientEmailElement );
setShippingAddressNoticeVisibility( true );
if ( $( 'form.checkout' ).length !== 0 ) {

View File

@ -1,13 +1,11 @@
jQuery( function ( $ ) {
const $modals = $( '.wcs-modal' );
let $currentModal;
let $triggerElement;
const modals = $( '.wcs-modal' );
// Resize all open modals on window resize.
$( window ).on( 'resize', resizeModals );
// Initialize modals
$( $modals ).each( function () {
$( modals ).each( function () {
trigger = $( this ).data( 'modal-trigger' );
$( trigger ).on( 'click', { modal: this }, show_modal );
} );
@ -20,35 +18,34 @@ jQuery( function ( $ ) {
* @param {JQuery event} event
*/
function show_modal( event ) {
$triggerElement = $( event.target );
$currentModal = $( event.data.modal );
const modal = $( event.data.modal );
if ( ! should_show_modal( $currentModal ) ) {
if ( ! should_show_modal( modal ) ) {
return;
}
// Prevent the trigger element event being triggered.
event.preventDefault();
const $contentWrapper = $currentModal.find( '.content-wrapper' );
const $close = $currentModal.find( '.close' );
const contentWrapper = modal.find( '.content-wrapper' );
const close = modal.find( '.close' );
$currentModal.addClass( 'open' );
resizeModal( $currentModal );
modal.trigger( 'focus' );
modal.addClass( 'open' );
resizeModal( modal );
$( document.body ).toggleClass( 'wcs-modal-open', true );
$currentModal.focus();
document.addEventListener( 'focusin', keepFocusInModal );
// Attach callbacks to handle closing the modal.
$close.on( 'click', () => close_modal( $currentModal ) );
$currentModal.on( 'click', () => close_modal( $currentModal ) );
$contentWrapper.on( 'click', ( e ) => e.stopPropagation() );
close.on( 'click', () => close_modal( modal ) );
modal.on( 'click', () => close_modal( modal ) );
contentWrapper.on( 'click', ( e ) => e.stopPropagation() );
// Close the modal if the escape key is pressed.
$currentModal.on( 'keyup', function ( e ) {
modal.on( 'keyup', function ( e ) {
if ( 27 === e.keyCode ) {
close_modal( $currentModal );
close_modal( modal );
}
} );
}
@ -56,17 +53,14 @@ jQuery( function ( $ ) {
/**
* Closes a modal and resets any forced height styles.
*
* @param {JQuery Object} $modal
* @param {JQuery Object} modal
*/
function close_modal( $modal ) {
$modal.removeClass( 'open' );
$( $modal ).find( '.content-wrapper' ).css( 'height', '' );
function close_modal( modal ) {
modal.removeClass( 'open' );
$( modal ).find( '.content-wrapper' ).css( 'height', '' );
if ( 0 === $modals.filter( '.open' ).length ) {
if ( 0 === modals.filter( '.open' ).length ) {
$( document.body ).removeClass( 'wcs-modal-open' );
$currentModal = false;
document.removeEventListener( 'focusin', keepFocusInModal );
$triggerElement.focus();
}
}
@ -92,7 +86,7 @@ jQuery( function ( $ ) {
* Resize all open modals to fit the display.
*/
function resizeModals() {
$( $modals ).each( function () {
$( modals ).each( function () {
if ( ! $( this ).hasClass( 'open' ) ) {
return;
}
@ -104,28 +98,17 @@ jQuery( function ( $ ) {
/**
* Resize a modal to fit the display.
*
* @param {JQuery Object} $modal
* @param {JQuery Object} modal
*/
function resizeModal( $modal ) {
const $modal_container = $( $modal ).find( '.content-wrapper' );
function resizeModal( modal ) {
var modal_container = $( modal ).find( '.content-wrapper' );
// On smaller displays the height is already forced to be 100% in CSS. We just clear any height we might set previously.
if ( $( window ).width() <= 414 ) {
$modal_container.css( 'height', '' );
} else if ( $modal_container.height() > $( window ).height() ) {
modal_container.css( 'height', '' );
} else if ( modal_container.height() > $( window ).height() ) {
// Force the container height to trigger scroll etc if it doesn't fit on the screen.
$modal_container.css( 'height', '90%' );
}
}
/**
* If focus moves out of the open modal, return focus to it.
*
* @param event
*/
function keepFocusInModal( event ) {
if ( $currentModal && ! $currentModal[0].contains( event.target ) ) {
$currentModal.focus();
modal_container.css( 'height', '90%' );
}
}
} );

View File

@ -1 +0,0 @@
<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-i18n', 'wp-primitives'), 'version' => 'bf7f65f22cfa80fe9522');

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
<?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

View File

@ -1,3 +1,2 @@
.wcs-recurring-totals-panel{padding:1em 0 0;position:relative}.wcs-recurring-totals-panel:after{border-style:solid;border-width:1px 0;bottom:0;content:"";display:block;right:0;opacity:.3;pointer-events:none;position:absolute;left:0;top:0}.wcs-recurring-totals-panel+.wcs-recurring-totals-panel:after{border-top-width:0}.wcs-recurring-totals-panel .wc-block-components-panel .wc-block-components-totals-item{padding-right:0;padding-left:0}.wcs-recurring-totals-panel .wc-block-components-totals-item__label:first-letter{text-transform:capitalize}.wcs-recurring-totals-panel .wcs-recurring-totals-panel__title .wc-block-components-totals-item__label{font-weight:500}.wcs-recurring-totals-panel__title{margin:0}.wc-block-components-main .wcs-recurring-totals-panel__details{padding:0 16px}.wcs-recurring-totals-panel__details .wc-block-components-panel__button,.wcs-recurring-totals-panel__details .wc-block-components-panel__button:focus,.wcs-recurring-totals-panel__details .wc-block-components-panel__button:hover{font-size:.875em}.wcs-recurring-totals-panel__details .wc-block-components-panel__content>.wc-block-components-totals-item:first-child{margin-top:0}.wcs-recurring-totals-panel__details .wc-block-components-panel__content>.wc-block-components-totals-item:last-child{margin-bottom:0}.wcs-recurring-totals-panel__details .wcs-recurring-totals-panel__details-total .wc-block-components-totals-item__label{font-weight:700}.wcs-recurring-totals__subscription-length{float:left}
.wc-block-components-local-pickup-rates-control .wc-block-components-local-pickup-select:not(:last-child){margin-bottom:16px}
.wcs-recurring-totals-panel{padding:1em 0 0;position:relative}.wcs-recurring-totals-panel:after{border-style:solid;border-width:1px 0;bottom:0;content:"";display:block;right:0;opacity:.3;pointer-events:none;position:absolute;left:0;top:0}.wcs-recurring-totals-panel+.wcs-recurring-totals-panel:after{border-top-width:0}.wcs-recurring-totals-panel .wc-block-components-panel .wc-block-components-totals-item{padding-right:0;padding-left:0}.wcs-recurring-totals-panel .wc-block-components-totals-item__label:first-letter{text-transform:capitalize}.wcs-recurring-totals-panel .wcs-recurring-totals-panel__title .wc-block-components-totals-item__label{font-weight:700}.wcs-recurring-totals-panel__title{margin:0}.wcs-recurring-totals-panel__details .wc-block-components-panel__button,.wcs-recurring-totals-panel__details .wc-block-components-panel__button:focus,.wcs-recurring-totals-panel__details .wc-block-components-panel__button:hover{font-size:.875em}.wcs-recurring-totals-panel__details .wc-block-components-panel__content>.wc-block-components-totals-item:first-child{margin-top:0}.wcs-recurring-totals-panel__details .wc-block-components-panel__content>.wc-block-components-totals-item:last-child{margin-bottom:0}.wcs-recurring-totals-panel__details .wcs-recurring-totals-panel__details-total .wc-block-components-totals-item__label{font-weight:700}.wcs-recurring-totals__subscription-length{float:left}

View File

@ -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' => '32bfa486d902830f29c7');
<?php return array('dependencies' => array('react', 'wc-blocks-checkout', 'wc-price-format', 'wc-settings', 'wp-element', 'wp-i18n', 'wp-plugins'), 'version' => '33d4c418a7e0cdfdfdd5');

View File

@ -1,3 +1,2 @@
.wcs-recurring-totals-panel{padding:1em 0 0;position:relative}.wcs-recurring-totals-panel:after{border-style:solid;border-width:1px 0;bottom:0;content:"";display:block;left:0;opacity:.3;pointer-events:none;position:absolute;right:0;top:0}.wcs-recurring-totals-panel+.wcs-recurring-totals-panel:after{border-top-width:0}.wcs-recurring-totals-panel .wc-block-components-panel .wc-block-components-totals-item{padding-left:0;padding-right:0}.wcs-recurring-totals-panel .wc-block-components-totals-item__label:first-letter{text-transform:capitalize}.wcs-recurring-totals-panel .wcs-recurring-totals-panel__title .wc-block-components-totals-item__label{font-weight:500}.wcs-recurring-totals-panel__title{margin:0}.wc-block-components-main .wcs-recurring-totals-panel__details{padding:0 16px}.wcs-recurring-totals-panel__details .wc-block-components-panel__button,.wcs-recurring-totals-panel__details .wc-block-components-panel__button:focus,.wcs-recurring-totals-panel__details .wc-block-components-panel__button:hover{font-size:.875em}.wcs-recurring-totals-panel__details .wc-block-components-panel__content>.wc-block-components-totals-item:first-child{margin-top:0}.wcs-recurring-totals-panel__details .wc-block-components-panel__content>.wc-block-components-totals-item:last-child{margin-bottom:0}.wcs-recurring-totals-panel__details .wcs-recurring-totals-panel__details-total .wc-block-components-totals-item__label{font-weight:700}.wcs-recurring-totals__subscription-length{float:right}
.wc-block-components-local-pickup-rates-control .wc-block-components-local-pickup-select:not(:last-child){margin-bottom:16px}
.wcs-recurring-totals-panel{padding:1em 0 0;position:relative}.wcs-recurring-totals-panel:after{border-style:solid;border-width:1px 0;bottom:0;content:"";display:block;left:0;opacity:.3;pointer-events:none;position:absolute;right:0;top:0}.wcs-recurring-totals-panel+.wcs-recurring-totals-panel:after{border-top-width:0}.wcs-recurring-totals-panel .wc-block-components-panel .wc-block-components-totals-item{padding-left:0;padding-right:0}.wcs-recurring-totals-panel .wc-block-components-totals-item__label:first-letter{text-transform:capitalize}.wcs-recurring-totals-panel .wcs-recurring-totals-panel__title .wc-block-components-totals-item__label{font-weight:700}.wcs-recurring-totals-panel__title{margin:0}.wcs-recurring-totals-panel__details .wc-block-components-panel__button,.wcs-recurring-totals-panel__details .wc-block-components-panel__button:focus,.wcs-recurring-totals-panel__details .wc-block-components-panel__button:hover{font-size:.875em}.wcs-recurring-totals-panel__details .wc-block-components-panel__content>.wc-block-components-totals-item:first-child{margin-top:0}.wcs-recurring-totals-panel__details .wc-block-components-panel__content>.wc-block-components-totals-item:last-child{margin-bottom:0}.wcs-recurring-totals-panel__details .wcs-recurring-totals-panel__details-total .wc-block-components-totals-item__label{font-weight:700}.wcs-recurring-totals__subscription-length{float:right}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
.wcsg-gifting-to-container-editing{display:flex;gap:5px;margin-top:10px;min-width:210px}.wcsg-gifting-to-container-editing .wc-block-components-text-input{flex-grow:1;margin-top:0}.wcsg-gifting-to-container-editing .wp-element-button.gifting-update-button:not(.is-link){min-height:unset;padding:0 var(--xs,20px)}.wcsg-gifting-to-container-editing .components-base-control__field{margin-bottom:0}.wcsg-gifting-to-container-editing .wc-block-components-text-input input:-webkit-autofill{padding-top:.5em}.wcsg-gifting-to-container-editing .has-error .components-text-control__input{color:var(--wc-red,#cc1818)}.wp-block-woocommerce-mini-cart-contents .wcsg-gifting-to-container-editing{flex-direction:column}.wp-block-woocommerce-mini-cart-contents .gifting-update-button{height:40px}.wcsg-gifting-to-container-view{align-items:center;display:flex;flex-wrap:wrap;gap:5px;max-width:100%}.wcsg-gifting-to-container-view span{align-items:center;display:inline-flex;max-width:100%;word-break:break-all;word-wrap:break-word;gap:5px}.wcsg-gifting-to-container-view .components-button.is-link{color:var(--wp--preset--color--contrast);flex-shrink:0;font-size:medium}.wc-block-checkout,.wc-block-components-product-details__gifting-to{align-items:center;display:flex;flex-wrap:wrap;max-width:100%}.wc-block-checkout .wc-block-components-product-details__name:after,.wc-block-components-product-details__gifting-to .wc-block-components-product-details__name:after{content:" "}.wc-block-checkout .wc-block-components-product-details__value,.wc-block-components-product-details__gifting-to .wc-block-components-product-details__value{align-items:center;display:inline-flex;max-width:100%;word-break:break-all;word-wrap:break-word}.wcsg-block-recipient-container{font-size:var(--wp--preset--font-size--small,14px);line-height:20px}.wcsg-block-recipient-container .components-checkbox-control__label{font-size:var(--wp--preset--font-size--small,14px);margin-bottom:0}.wc-block-cart .wc-block-components-product-details__gifting-to,.wc-block-cart .wc-block-components-product-details__gifting-to-hidden,.wc-block-cart .wc-block-components-product-details__item-key,.wc-block-checkout .wc-block-components-product-details__gifting-to-hidden,.wc-block-checkout .wc-block-components-product-details__item-key,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__gifting-to,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__gifting-to-hidden,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__item-key{display:none}
.wcsg-gifting-to-container-editing{display:flex;gap:5px;margin-top:12px}.wcsg-gifting-to-container-editing .wc-block-components-text-input{flex-grow:1;margin-top:0}.wcsg-gifting-to-container-editing .wp-element-button.gifting-update-button:not(.is-link){min-height:unset;padding:0 var(--xs,20px)}.wcsg-gifting-to-container-editing .components-base-control__field{margin-bottom:0}.wcsg-gifting-to-container-editing .wc-block-components-text-input input:-webkit-autofill{padding-top:.5em}.wcsg-gifting-to-container-editing .has-error .components-text-control__input{color:var(--wc-red,#cc1818)}.wp-block-woocommerce-mini-cart-contents .wcsg-gifting-to-container-editing{flex-direction:column}.wp-block-woocommerce-mini-cart-contents .gifting-update-button{height:40px}.wcsg-gifting-to-container-view{align-items:center;display:flex;flex-wrap:wrap;gap:5px;max-width:100%}.wcsg-gifting-to-container-view span{align-items:center;display:inline-flex;max-width:100%;word-break:break-all;word-wrap:break-word;gap:5px}.wcsg-gifting-to-container-view .components-button.is-link{color:var(--wp--preset--color--contrast);flex-shrink:0;font-size:medium}.wc-block-checkout,.wc-block-components-product-details__gifting-to{align-items:center;display:flex;flex-wrap:wrap;max-width:100%}.wc-block-checkout .wc-block-components-product-details__name:after,.wc-block-components-product-details__gifting-to .wc-block-components-product-details__name:after{content:" "}.wc-block-checkout .wc-block-components-product-details__value,.wc-block-components-product-details__gifting-to .wc-block-components-product-details__value{align-items:center;display:inline-flex;max-width:100%;word-break:break-all;word-wrap:break-word}.wcsg-block-recipient-container .components-checkbox-control__label{font-size:medium}.wc-block-cart .wc-block-components-product-details__gifting-to,.wc-block-cart .wc-block-components-product-details__gifting-to-hidden,.wc-block-cart .wc-block-components-product-details__item-key,.wc-block-checkout .wc-block-components-product-details__gifting-to-hidden,.wc-block-checkout .wc-block-components-product-details__item-key,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__gifting-to,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__gifting-to-hidden,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__item-key{display:none}

View File

@ -1 +1 @@
<?php return array('dependencies' => array('react', 'react-dom', 'wc-blocks-checkout', 'wp-components', 'wp-data', 'wp-i18n', 'wp-plugins', 'wp-url'), 'version' => 'cf4dd71c0c7418b591fd');
<?php return array('dependencies' => array('react', 'react-dom', 'wc-blocks-checkout', 'wp-components', 'wp-data', 'wp-i18n', 'wp-plugins', 'wp-url'), 'version' => 'e5022699093de48ed96e');

View File

@ -1 +1 @@
.wcsg-gifting-to-container-editing{display:flex;gap:5px;margin-top:10px;min-width:210px}.wcsg-gifting-to-container-editing .wc-block-components-text-input{flex-grow:1;margin-top:0}.wcsg-gifting-to-container-editing .wp-element-button.gifting-update-button:not(.is-link){min-height:unset;padding:0 var(--xs,20px)}.wcsg-gifting-to-container-editing .components-base-control__field{margin-bottom:0}.wcsg-gifting-to-container-editing .wc-block-components-text-input input:-webkit-autofill{padding-top:.5em}.wcsg-gifting-to-container-editing .has-error .components-text-control__input{color:var(--wc-red,#cc1818)}.wp-block-woocommerce-mini-cart-contents .wcsg-gifting-to-container-editing{flex-direction:column}.wp-block-woocommerce-mini-cart-contents .gifting-update-button{height:40px}.wcsg-gifting-to-container-view{align-items:center;display:flex;flex-wrap:wrap;gap:5px;max-width:100%}.wcsg-gifting-to-container-view span{align-items:center;display:inline-flex;max-width:100%;word-break:break-all;word-wrap:break-word;gap:5px}.wcsg-gifting-to-container-view .components-button.is-link{color:var(--wp--preset--color--contrast);flex-shrink:0;font-size:medium}.wc-block-checkout,.wc-block-components-product-details__gifting-to{align-items:center;display:flex;flex-wrap:wrap;max-width:100%}.wc-block-checkout .wc-block-components-product-details__name:after,.wc-block-components-product-details__gifting-to .wc-block-components-product-details__name:after{content:" "}.wc-block-checkout .wc-block-components-product-details__value,.wc-block-components-product-details__gifting-to .wc-block-components-product-details__value{align-items:center;display:inline-flex;max-width:100%;word-break:break-all;word-wrap:break-word}.wcsg-block-recipient-container{font-size:var(--wp--preset--font-size--small,14px);line-height:20px}.wcsg-block-recipient-container .components-checkbox-control__label{font-size:var(--wp--preset--font-size--small,14px);margin-bottom:0}.wc-block-cart .wc-block-components-product-details__gifting-to,.wc-block-cart .wc-block-components-product-details__gifting-to-hidden,.wc-block-cart .wc-block-components-product-details__item-key,.wc-block-checkout .wc-block-components-product-details__gifting-to-hidden,.wc-block-checkout .wc-block-components-product-details__item-key,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__gifting-to,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__gifting-to-hidden,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__item-key{display:none}
.wcsg-gifting-to-container-editing{display:flex;gap:5px;margin-top:12px}.wcsg-gifting-to-container-editing .wc-block-components-text-input{flex-grow:1;margin-top:0}.wcsg-gifting-to-container-editing .wp-element-button.gifting-update-button:not(.is-link){min-height:unset;padding:0 var(--xs,20px)}.wcsg-gifting-to-container-editing .components-base-control__field{margin-bottom:0}.wcsg-gifting-to-container-editing .wc-block-components-text-input input:-webkit-autofill{padding-top:.5em}.wcsg-gifting-to-container-editing .has-error .components-text-control__input{color:var(--wc-red,#cc1818)}.wp-block-woocommerce-mini-cart-contents .wcsg-gifting-to-container-editing{flex-direction:column}.wp-block-woocommerce-mini-cart-contents .gifting-update-button{height:40px}.wcsg-gifting-to-container-view{align-items:center;display:flex;flex-wrap:wrap;gap:5px;max-width:100%}.wcsg-gifting-to-container-view span{align-items:center;display:inline-flex;max-width:100%;word-break:break-all;word-wrap:break-word;gap:5px}.wcsg-gifting-to-container-view .components-button.is-link{color:var(--wp--preset--color--contrast);flex-shrink:0;font-size:medium}.wc-block-checkout,.wc-block-components-product-details__gifting-to{align-items:center;display:flex;flex-wrap:wrap;max-width:100%}.wc-block-checkout .wc-block-components-product-details__name:after,.wc-block-components-product-details__gifting-to .wc-block-components-product-details__name:after{content:" "}.wc-block-checkout .wc-block-components-product-details__value,.wc-block-components-product-details__gifting-to .wc-block-components-product-details__value{align-items:center;display:inline-flex;max-width:100%;word-break:break-all;word-wrap:break-word}.wcsg-block-recipient-container .components-checkbox-control__label{font-size:medium}.wc-block-cart .wc-block-components-product-details__gifting-to,.wc-block-cart .wc-block-components-product-details__gifting-to-hidden,.wc-block-cart .wc-block-components-product-details__item-key,.wc-block-checkout .wc-block-components-product-details__gifting-to-hidden,.wc-block-checkout .wc-block-components-product-details__item-key,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__gifting-to,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__gifting-to-hidden,.wp-block-woocommerce-mini-cart-contents .wc-block-components-product-details__item-key{display:none}

File diff suppressed because one or more lines are too long

View File

@ -1,82 +1,7 @@
*** WooCommerce Subscriptions Changelog ***
2026-01-12 - version 8.3.1
* Fix: Resolved an issue that was preventing limited subscription renewals.
* Fix: Resolved a React-error that may occur when loading the spotlight welcome announcement for gifting.
* Fix: Updated WooCommerce's CSV product importer to correctly update subscription product prices.
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.
* Added button roles.
* Added labels to remove product links.
* Adding missing header labels on related orders table.
* Prevented various hidden elements from being exposed to screen readers.
* Fixed focus problems for the renewal dialog.
* Fixed focus when closing modals.
* Fixed toggle state change using spacebar.
* Fixed label for sign up now button.
* Fixed toggle aria-label text to reflect state.
* Added aria-haspopup attribute to buttons that open modals.
* Fixed link for view order aria-label.
* Fixed renew now button text and aria-label.
* Added aria-modal and role attributes to modals and dialogs.
* Fix: Resubscribe button now appears correctly for cancelled limited subscriptions.
* Fix: Missing translation for pricing string on languages without plural form.
* Fix: Customized subscriptions links in WooCommerce emails now work correctly.
* Fix: Possible fatal errors when switching grouped subscriptions.
* Fix: Prevent unnecessary log entries related to gifted subscriptions.
* Fix: Make it easier for translation plugins to respect individual customer language preferences when sending subscription emails.
* Fix: Prevent recurring local pickup options from displaying for virtual subscriptions when the cart contains physical products.
* Fix: Prevent shipping summary from displaying on Blocks Checkout for virtual subscriptions.
* Fix: Item price in blocks checkout is correctly displayed without "due today" words for items with zero sign up fee.
2025-11-13 - version 8.1.0
* Fix: Prevent a fatal error that can occur when previewing emails in WooCommerce email settings.
* Fix: Prevent technical subscription-specific discount types from appearing in the coupon edit UI.
* Fix: Add admin notice and debug tool to recreate the payment retry database table when it's missing.
* Fix: Prevent resubscribe and reactivate buttons from showing for subscriptions containing disabled, deleted, draft, or limited products.
* Fix: Prevent error that blocks the subscriptions list from loading when gifted subscriptions are purchased with certain payment methods.
* Fix: Ensure gift recipient email address is displayed in the order confirmation email sent to the purchaser.
* Fix: Prevent pickup location options from displaying incorrectly alongside shipping methods on the Blocks Checkout page.
* Fix: Fix invalid HTML tags that cause fatal errors when using themes with WordPress Interactivity API.
* Fix: Add password reset links to the new account email template for gifted subscription recipients.
* Tweak: Improve the gift recipient email input field on Blocks Cart and Mini Cart to match the style of other checkout fields.
* Dev: Improve housekeeping of output buffers when handling requests to change subscription payment methods.
* Dev: Improve type safety in relation to the `wcs_get_subscription()` function.
2025-10-15 - version 8.0.0
* Add: Blocks Checkout now displays a single shipping method selection for both initial and recurring carts, similar to Classic Checkout. This can be disabled using the `wcs_cart_totals_shipping_html_price_only` filter.
* Fix: Resubscribing to a limited subscription from the product page will now reactivate the existing subscription.
* Fix: "New initial order - Recipient" email template preview no longer causes critical error.
* Fix: Shipping value on blocks checkout no longer shows as "Free" when no shipping method is available.
* Dev: Improved data telemetry which is sent to WooCommerce:
* WooCommerce Subscriptions setting values
* Order totals of subscription-related orders by order type and payment gateway, aggregated by month
* Number of subscription-related orders by order type and payment gateway, aggregated by month
* Number of products in parent subscription orders, aggregated by month
* Number of non-zero value parent subscription orders, aggregated by month
* Number of subscription products by billing interval
* Number of giftable subscription products
* Number of subscriptions by payment method, billing period, and renewal type
* Number of gifted subscriptions
* Number of subscribers
* Purchase event for subscription products
* Exclude orders with a "processing" status from the "subscription_orders" telemetry data.
2025-09-16 - version 7.9.0
* Add: REST API endpoint to retrieve subscriptions associated with a specific order ID.
* Fix: Keep subscription with successful renewals active when parent order fails.
* Fix: Error when renewing subscriptions with downloadable gifts.

View File

@ -1,59 +0,0 @@
<?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'
);
}
}

View File

@ -24,7 +24,6 @@ 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',
];
/**

View File

@ -83,10 +83,6 @@ class WC_REST_Subscriptions_V1_Controller extends WC_REST_Orders_V1_Controller {
if ( ! empty( $post->post_type ) && ! empty( $post->ID ) && 'shop_subscription' == $post->post_type ) {
$subscription = wcs_get_subscription( $post->ID );
if ( ! $subscription ) {
return $response;
}
$response->data['billing_period'] = $subscription->get_billing_period();
$response->data['billing_interval'] = $subscription->get_billing_interval();
@ -296,13 +292,8 @@ class WC_REST_Subscriptions_V1_Controller extends WC_REST_Orders_V1_Controller {
return new WP_Error( 'woocommerce_rest_invalid_shop_subscription_id', __( 'Invalid subscription id.', 'woocommerce-subscriptions' ), array( 'status' => 404 ) );
}
$this->post_type = 'shop_order';
$subscription = wcs_get_subscription( $id );
if ( ! $subscription ) {
return new WP_Error( 'woocommerce_rest_invalid_shop_subscription_id', __( 'Invalid subscription id.', 'woocommerce-subscriptions' ), array( 'status' => 404 ) );
}
$this->post_type = 'shop_order';
$subscription = wcs_get_subscription( $id );
$subscription_orders = $subscription->get_related_orders();
$orders = array();
@ -380,10 +371,6 @@ class WC_REST_Subscriptions_V1_Controller extends WC_REST_Orders_V1_Controller {
// In particular, the update_post_meta() called while _stripe_card_id is updated to _stripe_source_id
$subscription = wcs_get_subscription( $subscription->get_id() );
if ( ! $subscription ) {
throw new WC_REST_Exception( 'woocommerce_rest_payment_update_failed', __( 'Subscription payment method could not be set updated due to technical issues.', 'woocommerce-subscriptions' ), 500 );
}
if ( isset( $payment_method_meta[ $payment_method ] ) ) {
$payment_method_meta = $payment_method_meta[ $payment_method ];

View File

@ -702,16 +702,12 @@ class WC_REST_Subscriptions_V2_Controller extends WC_REST_Orders_V2_Controller {
$subscription->add_item( $item );
}
/*
* Fetch a fresh instance of the subscription because the current instance has an empty line item cache generated before we had copied the line items.
* Fetching a new instance will ensure the line items are used when calculating totals.
*/
$subscription = wcs_get_subscription( $subscription->get_id() );
if ( ! $subscription ) {
throw new Exception( __( 'There was a problem completing this request. The subscription may have been deleted by another process.', 'woocommerce-subscriptions' ) );
}
$subscription->calculate_totals();
/**
@ -723,13 +719,7 @@ class WC_REST_Subscriptions_V2_Controller extends WC_REST_Orders_V2_Controller {
*/
do_action( "woocommerce_rest_insert_{$this->post_type}_object", $subscription, $request, true );
$fresh_subscription = wcs_get_subscription( $subscription->get_id() );
if ( ! $fresh_subscription ) {
throw new Exception( __( 'There was a problem completing this request. The subscription may have been deleted by another process.', 'woocommerce-subscriptions' ) );
}
$response = $this->prepare_object_for_response( $fresh_subscription, $request );
$response = $this->prepare_object_for_response( wcs_get_subscription( $subscription->get_id() ), $request );
$subscriptions[] = $this->prepare_response_for_collection( $response );
}
} catch ( Exception $e ) {

View File

@ -117,13 +117,6 @@ class WC_Subscriptions_Dependency_Manager {
$this->wc_version_cached = true;
// Try to get version from transient first
$this->wc_active_version = get_transient( 'wcs_woocommerce_active_version' );
if ( false !== $this->wc_active_version ) {
return $this->wc_active_version;
}
// Load plugin.php if it's not already loaded.
if ( ! function_exists( 'is_plugin_active' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
@ -146,15 +139,9 @@ class WC_Subscriptions_Dependency_Manager {
if ( $is_woocommerce && is_plugin_active( $plugin_slug ) ) {
$this->wc_active_version = $plugin_data['Version'];
break; // Found it, no need to continue looping
}
}
// Cache the result in a transient for 1 hour
if ( ! empty( $this->wc_active_version ) ) {
set_transient( 'wcs_woocommerce_active_version', $this->wc_active_version, HOUR_IN_SECONDS );
}
return $this->wc_active_version;
}

View File

@ -8,8 +8,6 @@
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce_Subscriptions\Internal\Telemetry\Events as WC_Tracks_Events;
/**
* @method static WC_Subscriptions_Plugin instance()
*/
@ -34,10 +32,6 @@ 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();
if ( defined( 'WP_CLI' ) && WP_CLI ) {
new WC_Subscriptions_CLI();
@ -45,8 +39,6 @@ class WC_Subscriptions_Plugin extends WC_Subscriptions_Core_Plugin {
add_action( 'admin_enqueue_scripts', array( $this, 'maybe_show_welcome_message' ) );
add_action( 'plugins_loaded', array( $this, 'init_gifting' ) );
add_action( 'plugins_loaded', array( $this, 'init_downloads' ) );
add_action( 'admin_notices', array( WC_Subscription_Downloads_Settings::class, 'add_notice_about_bundled_feature' ) );
}
/**
@ -289,34 +281,6 @@ class WC_Subscriptions_Plugin extends WC_Subscriptions_Core_Plugin {
WCS_Gifting::init();
}
/**
* Attempts to initialize additional downloads functionality.
*
* This functionality makes it possible to link downloadable products with a subscription
* product. Purchasers of the subscription product then automatically get access to the files associated with downloadable product.
*
* Previously, this functionality existed as a standalone plugin (WooCommerce Subscription Downnloads) and so,
* before initializing, we try to determine if the standalone plugin is active and has already loaded (if it is
* active, we do not proceed).
*/
public function init_downloads() {
if (
$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;
}
WC_Subscription_Downloads::setup();
}
/**
* Tries to determine if the specified plugin is being activated.
*

View File

@ -45,7 +45,6 @@ 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,
);

View File

@ -30,7 +30,7 @@ class WCS_Call_To_Action_Button_Text_Manager {
public static function add_settings( $settings ) {
$button_text_settings = array(
array(
'name' => __( 'Button text', 'woocommerce-subscriptions' ),
'name' => __( 'Button Text', 'woocommerce-subscriptions' ),
'type' => 'title',
'desc' => '',
'id' => WC_Subscriptions_Admin::$option_prefix . '_button_text',

View File

@ -357,12 +357,7 @@ class WCS_Limited_Recurring_Coupon_Manager {
$has_limited_coupon = false;
if ( $change_payment && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) ) ) {
$subscription = wcs_get_subscription( $change_payment );
if ( ! $subscription ) {
return $gateways;
}
$subscription = wcs_get_subscription( $change_payment );
$has_limited_coupon = self::order_has_limited_recurring_coupon( $subscription );
}

View File

@ -74,10 +74,7 @@ class WCS_Subscriber_Role_Manager {
),
);
if ( ! WC_Subscriptions_Admin::insert_setting_after( $settings, WC_Subscriptions_Admin::$option_prefix . '_button_text', $role_settings, 'multiple_settings', 'sectionend' ) ) {
$settings = array_merge( $settings, $role_settings );
}
WC_Subscriptions_Admin::insert_setting_after( $settings, WC_Subscriptions_Admin::$option_prefix . '_button_text', $role_settings, 'multiple_settings', 'sectionend' );
return $settings;
}

View File

@ -138,7 +138,9 @@ class WCS_Webhooks {
throw new \Exception( 'The Legacy REST API plugin is not installed on this site. More information: https://developer.woocommerce.com/2023/10/03/the-legacy-rest-api-will-move-to-a-dedicated-extension-in-woocommerce-9-0/ ' );
}
// @phpstan-ignore-next-line
WC()->api->WC_API_Subscriptions->register_routes( array() );
// @phpstan-ignore-next-line
$payload = WC()->api->WC_API_Subscriptions->get_subscription( $resource_id );
break;
case 'wp_api_v1':

View File

@ -48,18 +48,6 @@ abstract class WCS_Table_Maker {
}
}
/**
* Deletes the schema option and recreates the tables.
*
* This forces the table schema to be regenerated by removing the stored
* schema version and triggering the table registration process.
*/
public function recreate_tables() {
delete_option( $this->get_schema_option_name() );
$this->register_tables();
}
/**
* @param string $table The name of the table
*

View File

@ -1736,8 +1736,6 @@ class WC_Subscriptions_Admin {
/**
* Adds Subscriptions specific details to the WooCommerce System Status report.
*
* @deprecated 2.2.2 Use WC_Subscriptions_Admin::render_system_status_items() instead.
*
* @param array $debug_data
* @return array
*/
@ -2143,8 +2141,6 @@ class WC_Subscriptions_Admin {
/**
* Renders the Subscription information in the WC status page
*
* @deprecated 2.3 Use WCS_Admin_System_Status::render_system_status_items() instead.
*/
public static function render_system_status_items() {
_deprecated_function( __METHOD__, '2.3', 'WCS_Admin_System_Status::render_system_status_items()' );
@ -2154,8 +2150,6 @@ class WC_Subscriptions_Admin {
/**
* Outputs the contents of the "Renewal Orders" meta box.
*
* @deprecated 2.0 Use WCS_Meta_Box_Related_Orders::output() instead.
*
* @param object $post Current post data.
*/
public static function related_orders_meta_box( $post ) {
@ -2166,8 +2160,6 @@ class WC_Subscriptions_Admin {
/**
* Add users with subscriptions to the "Customers" report in WooCommerce -> Reports.
*
* @deprecated 2.0
*
* @param WP_User_Query $user_query
*/
public static function add_subscribers_to_customers( $user_query ) {
@ -2177,7 +2169,6 @@ class WC_Subscriptions_Admin {
/**
* Set a translation safe screen ID for Subscriptions
*
* @deprecated 2.0
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.3.3
*/
public static function set_admin_screen_id() {
@ -2187,7 +2178,6 @@ class WC_Subscriptions_Admin {
/**
* Once we have set a correct admin page screen ID, we can use it for adding the Manage Subscriptions table's columns.
*
* @deprecated 2.0
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.3.3
*/
public static function add_subscriptions_table_column_filter() {
@ -2197,8 +2187,6 @@ class WC_Subscriptions_Admin {
/**
* Filter the "Orders" list to show only renewal orders associated with a specific parent order.
*
* @deprecated 2.0
*
* @param array $request
* @return array
*/
@ -2217,8 +2205,6 @@ class WC_Subscriptions_Admin {
/**
* Output the metabox
*
* @deprecated 2.0
*/
public static function recurring_totals_meta_box( $post ) {
_deprecated_function( __METHOD__, '2.0' );

View File

@ -519,7 +519,7 @@ class WCS_Admin_Post_Types {
'wcs-unknown-order-info-wrapper',
esc_url( 'https://woocommerce.com/document/subscriptions/store-manager-guide/#section-19' ),
// translators: Placeholder is a <br> HTML tag.
wcs_help_tip( sprintf( __( "This subscription couldn't be loaded from the database. %s Click to learn more.", 'woocommerce-subscriptions' ), '<br>' ) ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
wcs_help_tip( sprintf( __( "This subscription couldn't be loaded from the database. %s Click to learn more.", 'woocommerce-subscriptions' ), '</br>' ) ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
);
return;
}
@ -711,12 +711,12 @@ class WCS_Admin_Post_Types {
$tooltip_classes = 'woocommerce-help-tip';
if ( $datetime->getTimestamp() < time() ) {
$tooltip_message .= '<b>' . esc_html__( 'Subscription payment overdue.', 'woocommerce-subscriptions' ) . '</b><br>';
$tooltip_message .= __( '<b>Subscription payment overdue.</b></br>', 'woocommerce-subscriptions' );
$tooltip_classes .= ' wcs-payment-overdue';
}
if ( $subscription->payment_method_supports( 'gateway_scheduled_payments' ) && ! $subscription->is_manual() ) {
$tooltip_message .= esc_html__( 'This date should be treated as an estimate only. The payment gateway for this subscription controls when payments are processed.', 'woocommerce-subscriptions' ) . '<br>';
$tooltip_message .= __( 'This date should be treated as an estimate only. The payment gateway for this subscription controls when payments are processed.</br>', 'woocommerce-subscriptions' );
$tooltip_classes .= ' wcs-offsite-renewal';
}
@ -1171,7 +1171,7 @@ class WCS_Admin_Post_Types {
// @phpstan-ignore property.notFound
foreach ( WC()->payment_gateways->get_available_payment_gateways() as $gateway_id => $gateway ) {
echo '<option value="' . esc_attr( $gateway_id ) . '"' . ( $selected_gateway_id === $gateway_id ? 'selected' : '' ) . '>' . esc_html( method_exists( $gateway, 'get_title' ) ? $gateway->get_title() : $gateway->title ) . '</option>';
echo '<option value="' . esc_attr( $gateway_id ) . '"' . ( $selected_gateway_id === $gateway_id ? 'selected' : '' ) . '>' . esc_html( $gateway->title ) . '</option>';
}
echo '<option value="_manual_renewal">' . esc_html__( 'Manual Renewal', 'woocommerce-subscriptions' ) . '</option>';
?>
@ -1503,20 +1503,9 @@ class WCS_Admin_Post_Types {
foreach ( $subscription_ids as $subscription_id ) {
$subscription = wcs_get_subscription( $subscription_id );
$note = _x( 'Subscription status changed by bulk edit:', 'Used in order note. Reason why status changed.', 'woocommerce-subscriptions' );
$note = _x( 'Subscription status changed by bulk edit:', 'Used in order note. Reason why status changed.', 'woocommerce-subscriptions' );
try {
if ( ! $subscription ) {
throw new Exception(
sprintf(
// Translators: 1: subscription ID.
__( 'Subscription with ID %1$d does not exist and could not be updated.', 'woocommerce-subscriptions' ),
$subscription_id
)
);
}
if ( 'cancelled' === $new_status ) {
$subscription->cancel_order( $note );
} else {
@ -1553,11 +1542,6 @@ class WCS_Admin_Post_Types {
foreach ( $subscription_ids as $id ) {
$subscription = wcs_get_subscription( $id );
if ( ! $subscription ) {
continue;
}
$subscription->delete( $force_delete );
$updated_subscription = wcs_get_subscription( $id );
@ -1587,13 +1571,7 @@ class WCS_Admin_Post_Types {
foreach ( $subscription_ids as $id ) {
if ( $use_crud_method ) {
$subscription = wcs_get_subscription( $id );
if ( ! $subscription ) {
continue;
}
$data_store->untrash_order( $subscription );
$data_store->untrash_order( wcs_get_subscription( $id ) );
} else {
wp_untrash_post( $id );
}
@ -1890,6 +1868,7 @@ class WCS_Admin_Post_Types {
* @return string[] $pieces Updated associative array of clauses for the query.
*/
private function orders_table_clauses_low_performance( $pieces ) {
// @phpstan-ignore-next-line
$order_datastore = wc_get_container()->get( \Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore::class );
$order_table = $order_datastore::get_orders_table_name();
$meta_table = $order_datastore::get_meta_table_name();
@ -1920,6 +1899,7 @@ class WCS_Admin_Post_Types {
private function orders_table_clauses_high_performance( $pieces ) {
global $wpdb;
// @phpstan-ignore-next-line
$order_datastore = wc_get_container()->get( \Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore::class );
$order_table = $order_datastore::get_orders_table_name();
$meta_table = $order_datastore::get_meta_table_name();

View File

@ -20,7 +20,6 @@ class WCS_Admin_Product_Import_Export_Manager {
add_filter( 'woocommerce_exporter_product_types', array( __CLASS__, 'register_susbcription_variation_type' ) );
add_filter( 'woocommerce_product_export_product_query_args', array( __CLASS__, 'filter_export_query' ) );
add_filter( 'woocommerce_product_import_process_item_data', array( __CLASS__, 'import_subscription_variations' ) );
add_filter( 'woocommerce_product_import_pre_insert_product_object', array( __CLASS__, 'set_subscription_price_on_import' ) );
}
/**
@ -105,23 +104,4 @@ class WCS_Admin_Product_Import_Export_Manager {
return $data;
}
/**
* Sets the subscription price meta when importing a subscription product.
*
* During CSV imports, WooCommerce sets the regular price (`_price`) from the "Regular price" column,
* but subscription products also need _subscription_price to be set for proper and consistent pricing.
*
* @param WC_Product $product The product object being imported.
*
* @return WC_Product
*/
public static function set_subscription_price_on_import( $product ) {
if ( ! $product instanceof WC_Product || ! WC_Subscriptions_Product::is_subscription( $product ) ) {
return $product;
}
$product->update_meta_data( '_subscription_price', $product->get_regular_price() );
return $product;
}
}

View File

@ -24,7 +24,7 @@ if ( ! defined( 'ABSPATH' ) ) {
sprintf( // Translators: The %1 placeholder is the translated order relationship ("Parent Order"), %2 placeholder is a <br> HTML tag.
__( 'This %1$s couldn\'t be loaded from the database. %1$s Click to learn more.', 'woocommerce-subscriptions' ),
esc_html( $relationship ),
'<br>'
'</br>'
)
);
?>

View File

@ -17,6 +17,24 @@ if ( ! defined( 'ABSPATH' ) ) {
class WC_Product_Subscription_Variation extends WC_Product_Variation {
/**
* A way to access the old array property.
*/
protected $subscription_variation_level_meta_data;
/**
* Create a simple subscription product object.
*
* @access public
* @param mixed $product
*/
public function __construct( $product = 0 ) {
parent::__construct( $product );
$this->subscription_variation_level_meta_data = new WCS_Array_Property_Post_Meta_Black_Magic( $this->get_id() );
}
/**
* Magic __get method for backwards compatibility. Map legacy vars to WC_Subscriptions_Product getters.
*
@ -25,11 +43,19 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
*/
public function __get( $key ) {
$value = wcs_product_deprecated_property_handler( $key, $this );
if ( 'subscription_variation_level_meta_data' === $key ) {
// No matching property found in wcs_product_deprecated_property_handler()
if ( is_null( $value ) ) {
$value = parent::__get( $key );
wcs_deprecated_argument( __CLASS__ . '::$' . $key, '2.2.0', 'Product properties should not be accessed directly with WooCommerce 3.0+. Use the getter in WC_Subscriptions_Product instead.' );
$value = $this->subscription_variation_level_meta_data; // Behold, the horror that is the magic of WCS_Array_Property_Post_Meta_Black_Magic
} else {
$value = wcs_product_deprecated_property_handler( $key, $this );
// No matching property found in wcs_product_deprecated_property_handler()
if ( is_null( $value ) ) {
$value = parent::__get( $key );
}
}
return $value;
@ -63,6 +89,7 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
/**
* Get the add to cart button text
*
* @access public
* @return string
*/
public function add_to_cart_text() {
@ -79,6 +106,7 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
/**
* Get the add to cart button text for the single page
*
* @access public
* @return string
*/
public function single_add_to_cart_text() {
@ -88,6 +116,7 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
/**
* Checks if the variable product this variation belongs to is purchasable.
*
* @access public
* @return bool
*/
public function is_purchasable() {
@ -99,11 +128,12 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
* Checks the product type to see if it is either this product's type or the parent's
* product type.
*
* @access public
* @param mixed $type Array or string of types
* @return bool
*/
public function is_type( $type ) {
if ( 'variation' === $type || ( is_array( $type ) && in_array( 'variation', $type, true ) ) ) {
if ( 'variation' == $type || ( is_array( $type ) && in_array( 'variation', $type ) ) ) {
return true;
} else {
return parent::is_type( $type );
@ -115,8 +145,6 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
/**
* Return the sign-up fee for this product
*
* @deprecated 2.2.0 Use WC_Subscriptions_Product::get_sign_up_fee() instead.
*
* @return string
*/
public function get_sign_up_fee() {
@ -127,7 +155,6 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
/**
* Returns the sign up fee (including tax) by filtering the products price used in
* @see WC_Product::get_price_including_tax( $qty )
* @deprecated 2.2.0 Use wcs_get_price_including_tax() instead.
*
* @return string
*/
@ -147,8 +174,6 @@ class WC_Product_Subscription_Variation extends WC_Product_Variation {
* Returns the sign up fee (excluding tax) by filtering the products price used in
* @see WC_Product::get_price_excluding_tax( $qty )
*
* @deprecated 2.2.0 Use wcs_get_price_excluding_tax() instead.
*
* @return string
*/
public function get_sign_up_fee_excluding_tax( $qty = 1, $price = '' ) {

View File

@ -76,32 +76,6 @@ class WC_Product_Subscription extends WC_Product_Simple {
return apply_filters( 'woocommerce_product_add_to_cart_text', $text, $this );
}
/**
* Provides the descriptive text for add-to-cart buttons.
*
* @return mixed
*/
public function add_to_cart_description() {
if ( $this->is_purchasable() && $this->is_in_stock() ) {
// For accessibility reasons it is recommended that the aria-label is the same as, or else starts with, the
// same text that is visible on the button itself.
$text = sprintf(
// Translators: %1$s: Pre-determined add-to-cart text 2: Product title.
_x( '%1$s: &ldquo;%2$s&rdquo;', 'Add-to-cart button description', 'woocommerce-subscriptions' ),
WC_Subscriptions_Product::get_add_to_cart_text(),
$this->get_name()
);
} else {
$text = sprintf(
// Translators: %1$s: Product title.
__( 'Read more about &ldquo;%1$s&rdquo;', 'woocommerce-subscriptions' ),
$this->get_name()
);
}
return apply_filters( 'woocommerce_product_add_to_cart_description', $text, $this );
}
/**
* Get the add to cart button text for the single page
*
@ -130,8 +104,6 @@ class WC_Product_Subscription extends WC_Product_Simple {
/**
* Return the sign-up fee for this product
*
* @deprecated 2.2.0 Use WC_Subscriptions_Product::get_sign_up_fee().
*
* @return string
*/
public function get_sign_up_fee() {
@ -142,7 +114,6 @@ class WC_Product_Subscription extends WC_Product_Simple {
/**
* Returns the sign up fee (including tax) by filtering the products price used in
* @see WC_Product::get_price_including_tax( $qty )
* @deprecated 2.3.0
*
* @return string
*/
@ -160,7 +131,6 @@ class WC_Product_Subscription extends WC_Product_Simple {
/**
* Returns the sign up fee (excluding tax) by filtering the products price used in
* @see WC_Product::get_price_excluding_tax( $qty )
* @deprecated 2.2.0
*
* @return string
*/

View File

@ -44,14 +44,6 @@ class WC_Subscription extends WC_Order {
*/
private $editable;
/**
* Stores if the subscription is in the payment completed flow.
* Allowing subscriptions to be renewed even if they have limited products.
*
* @var bool
*/
private $is_payment_completed_flow = false;
/**
* Extra data for this object. Name value pairs (name + default value). Used to add additional information to parent.
*
@ -348,9 +340,7 @@ class WC_Subscription extends WC_Order {
break;
case 'completed': // core WC order status mapped internally to avoid exceptions
case 'active':
if ( ! $this->is_payment_completed_flow && $this->contains_unavailable_product() ) {
$can_be_updated = false;
} elseif ( $this->payment_method_supports( 'subscription_reactivation' ) && $this->has_status( 'on-hold' ) ) {
if ( $this->payment_method_supports( 'subscription_reactivation' ) && $this->has_status( 'on-hold' ) ) {
// If the subscription's end date is in the past, it cannot be reactivated.
$end_time = $this->get_time( 'end' );
if ( 0 !== $end_time && $end_time < gmdate( 'U' ) ) {
@ -425,48 +415,6 @@ class WC_Subscription extends WC_Order {
return apply_filters( 'woocommerce_can_subscription_be_updated_to_' . $new_status, $can_be_updated, $this );
}
/**
* Checks if the subscription contains an unavailable product.
*
* A product is considered unavailable if it is:
* - Deleted (not found)
* - Not published (draft, trash, private, etc.)
*
* Note: This method intentionally does NOT use is_purchasable() to avoid incorrectly
* flagging limited products as unavailable. Limited products return is_purchasable() = false
* for users with existing subscriptions, but they should still be available for resubscribe.
* Functions like wcs_can_user_resubscribe_to() have specific logic to handle limited products
* by checking if the user has an active subscription.
*
* @return bool
*/
public function contains_unavailable_product() {
/** @var WC_Order_Item_Product $line_item */
foreach ( $this->get_items() as $line_item ) {
$product = $line_item->get_product();
// Product doesn't exist (deleted).
if ( ! $product instanceof WC_Product ) {
return true;
}
// If the product is a subscription variation, use the parent product.
if ( $product->is_type( 'subscription_variation' ) ) {
$parent_product_id = $product->get_parent_id();
$product = wc_get_product( $parent_product_id );
}
// Check if product is published. Products with other statuses (draft, trash, private)
// are not available for purchase or resubscribe.
$product_status = $product->get_status();
if ( 'publish' !== $product_status ) {
return true;
}
}
return false;
}
/**
* Updates status of the subscription
*
@ -2048,7 +1996,6 @@ class WC_Subscription extends WC_Order {
* @param WC_Order $last_order
*/
public function payment_complete_for_order( $last_order ) {
$this->is_payment_completed_flow = true;
// Clear the cached renewal payment counts
if ( isset( $this->cached_payment_count['completed'] ) ) {

View File

@ -64,7 +64,6 @@ class WC_Subscriptions_Addresses {
$actions['change_address'] = array(
'url' => esc_url( add_query_arg( array( 'subscription' => $subscription->get_id() ), wc_get_endpoint_url( 'edit-address', 'shipping' ) ) ),
'name' => __( 'Change address', 'woocommerce-subscriptions' ),
'role' => 'link',
);
}
@ -313,11 +312,9 @@ class WC_Subscriptions_Addresses {
/**
* Update the address fields on an order
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.3
* @deprecated 2.2.0 Use WC_Order::set_address() or WC_Subscription::set_address()
*
* @param array $subscription A WooCommerce Subscription array
* @param array $address_fields Locale aware address fields of the form returned by WC_Countries->get_address_fields() for a given country
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.3
*/
public static function maybe_update_order_address( $subscription, $address_fields ) {
_deprecated_function( __METHOD__, '2.0', 'WC_Order::set_address() or WC_Subscription::set_address()' );

View File

@ -1088,11 +1088,21 @@ class WC_Subscriptions_Cart {
self::set_cached_recurring_cart( $recurring_cart );
foreach ( $recurring_cart->get_shipping_packages() as $recurring_cart_package_key => $recurring_cart_package ) {
if ( self::package_rates_match_initial_rates( $standard_packages, $recurring_cart_package, $recurring_cart_key, $recurring_cart ) ) {
continue;
$package_index = isset( $recurring_cart_package['package_index'] ) ? $recurring_cart_package['package_index'] : 0;
$package = WC()->shipping->calculate_shipping_for_package( $recurring_cart_package );
$package_rates_match = false;
if ( isset( $standard_packages[ $package_index ] ) ) {
// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
$package_rates_match = apply_filters( 'wcs_recurring_shipping_package_rates_match_standard_rates', $package['rates'] == $standard_packages[ $package_index ]['rates'], $package['rates'], $standard_packages[ $package_index ]['rates'], $recurring_cart_key );
}
$package = WC()->shipping->calculate_shipping_for_package( $recurring_cart_package );
if ( $package_rates_match ) {
// The recurring package rates match the initial package rates, there won't be a selected shipping method for this recurring cart package move on to the next package.
if ( apply_filters( 'wcs_cart_totals_shipping_html_price_only', true, $package, $recurring_cart ) ) {
continue;
}
}
// If the chosen shipping method is not available for this recurring cart package, display an error and unset the selected method.
if ( ! isset( $package['rates'][ $shipping_methods[ $recurring_cart_package_key ] ] ) ) {
@ -1117,47 +1127,6 @@ class WC_Subscriptions_Cart {
self::set_cached_recurring_cart( $cached_recurring_cart );
}
/**
* Checks if the recurring package rates match the initial package rates.
*
* @param array $standard_packages The standard packages.
* @param array $recurring_cart_package The recurring cart package.
* @param string $recurring_cart_key The recurring cart key.
* @param object $recurring_cart The recurring cart.
* @return bool Whether the recurring package rates match the initial package rates.
*/
public static function package_rates_match_initial_rates( $standard_packages, $recurring_cart_package, $recurring_cart_key, $recurring_cart ) {
$package_index = isset( $recurring_cart_package['package_index'] ) ? $recurring_cart_package['package_index'] : 0;
$package = WC()->shipping->calculate_shipping_for_package( $recurring_cart_package );
$package_rates_match = false;
if ( isset( $standard_packages[ $package_index ] ) ) {
// Ignore item names during the comparison.
$package_rates = array_map(
[ __CLASS__, 'remove_item_names_from_rate' ],
$package['rates']
);
$initial_rates = array_map(
[ __CLASS__, 'remove_item_names_from_rate' ],
$standard_packages[ $package_index ]['rates']
);
// phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual -- Order of array values is not important.
$package_rates_match = apply_filters( 'wcs_recurring_shipping_package_rates_match_standard_rates', $package_rates == $initial_rates, $package['rates'], $standard_packages[ $package_index ]['rates'], $recurring_cart_key );
}
if ( $package_rates_match ) {
/**
* The recurring package rates match the initial package rates, there won't be a selected shipping method for this recurring cart package move on to the next package.
*
* phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
*/
return apply_filters( 'wcs_cart_totals_shipping_html_price_only', true, $package, $recurring_cart );
}
return false;
}
/**
* Checks the cart to see if it contains a specific product.
*
@ -1517,7 +1486,7 @@ class WC_Subscriptions_Cart {
*/
public static function pre_get_refreshed_fragments() {
wcs_deprecated_function( __METHOD__, '2.5.0' );
if ( wp_doing_ajax() && ! defined( 'WOOCOMMERCE_CART' ) ) {
if ( defined( 'DOING_AJAX' ) && true === DOING_AJAX && ! defined( 'WOOCOMMERCE_CART' ) ) {
define( 'WOOCOMMERCE_CART', true );
WC()->cart->calculate_totals();
}
@ -2570,17 +2539,4 @@ class WC_Subscriptions_Cart {
return true;
}
/**
* Remove the item names from the shipping rate
* to check if the rates match.
*
* @param WC_Shipping_Rate $rate The rate.
* @return WC_Shipping_Rate The rate.
*/
private static function remove_item_names_from_rate( $rate ) {
$rate->add_meta_data( 'Items', null );
return $rate;
}
}

View File

@ -139,11 +139,6 @@ class WC_Subscriptions_Change_Payment_Gateway {
$subscription = wcs_get_subscription( absint( $wp->query_vars['order-pay'] ) );
$subscription_key = isset( $_GET['key'] ) ? wc_clean( $_GET['key'] ) : '';
if ( ! $subscription ) {
wc_print_notice( __( 'There was an unexpected problem with your request. Please try again.', 'woocommerce-subscriptions' ), 'error' );
return;
}
do_action( 'before_woocommerce_pay' );
/**
@ -281,27 +276,19 @@ class WC_Subscriptions_Change_Payment_Gateway {
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
*/
public static function change_payment_method_via_pay_shortcode() {
if ( ! isset( $_POST['_wcsnonce'] ) || ! wp_verify_nonce( wc_clean( wp_unslash( $_POST['_wcsnonce'] ) ), 'wcs_change_payment_method' ) ) {
return;
}
$subscription_id = absint( wc_clean( wp_unslash( $_POST['woocommerce_change_payment'] ) ) );
$subscription = wcs_get_subscription( $subscription_id );
if ( ! $subscription ) {
wc_add_notice( __( 'There was an unexpected problem with your request. Please try again.', 'woocommerce-subscriptions' ), 'error' );
return;
}
$subscription = wcs_get_subscription( $subscription_id );
do_action( 'woocommerce_subscription_change_payment_method_via_pay_shortcode', $subscription );
if ( ! $subscription instanceof WC_Subscription || $subscription->get_order_key() !== wc_clean( wp_unslash( $_GET['key'] ?? '' ) ) ) {
return;
}
ob_start();
try {
// We open an output buffer to suppress any error noise that might break the redirect.
$output_buffer_opened = ob_start();
if ( $subscription->get_order_key() == wc_clean( wp_unslash( $_GET['key'] ) ) ) {
$subscription_billing_country = $subscription->get_billing_country();
$subscription_billing_state = $subscription->get_billing_state();
@ -372,11 +359,6 @@ class WC_Subscriptions_Change_Payment_Gateway {
*/
$subscription = wcs_get_subscription( $subscription->get_id() );
if ( ! $subscription ) {
wc_add_notice( __( 'There was an unexpected problem with your request. Please try again.', 'woocommerce-subscriptions' ), 'error' );
return;
}
$subscription->set_requires_manual_renewal( false );
$subscription->save();
@ -401,13 +383,9 @@ class WC_Subscriptions_Change_Payment_Gateway {
wp_safe_redirect( $result['redirect'] );
exit;
}
} finally {
// Close the output buffer (if we exited, this will have happened automatically, so this is primarily here
// to ensure we clean-up in the event that we returned early).
if ( $output_buffer_opened ) {
ob_clean();
}
}
ob_get_clean();
}
/**
@ -617,12 +595,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
*/
if ( ! $is_change_payment_method_request && $cart_contains_failed_renewal ) {
$subscription = wcs_get_subscription( $renewal_order_cart_item['subscription_renewal']['subscription_id'] );
// If the subscription has been deleted (which is a reason $subscription may be false), we probably do not
// need to concern ourselves with a failed manual renewal.
if ( $subscription ) {
$cart_contains_failed_manual_renewal = $subscription->is_manual();
}
$cart_contains_failed_manual_renewal = $subscription->is_manual();
}
if ( apply_filters( 'wcs_payment_gateways_change_payment_method', $is_change_payment_method_request || ( $cart_contains_failed_renewal && ! $cart_contains_failed_manual_renewal ) ) ) {
@ -755,7 +728,6 @@ class WC_Subscriptions_Change_Payment_Gateway {
}
$subscription = wcs_get_subscription( absint( $wp->query_vars['order-pay'] ) );
if ( ! $subscription ) {
return $title;
}
@ -851,19 +823,19 @@ class WC_Subscriptions_Change_Payment_Gateway {
$subscription = wcs_get_subscription( absint( $wp->query_vars['order-pay'] ) );
if ( ! $subscription ) {
return $content;
if ( $subscription ) {
wc_add_notice( __( 'Please log in to your account below to choose a new payment method for your subscription.', 'woocommerce-subscriptions' ), 'notice' );
ob_start();
woocommerce_login_form( array(
'redirect' => $subscription->get_change_payment_method_url(),
'message' => wc_print_notices( true ),
) );
$content = ob_get_clean();
}
wc_add_notice( __( 'Please log in to your account below to choose a new payment method for your subscription.', 'woocommerce-subscriptions' ), 'notice' );
ob_start();
woocommerce_login_form( array(
'redirect' => $subscription->get_change_payment_method_url(),
'message' => wc_print_notices( true ),
) );
return ob_get_clean();
return $content;
}
/** Deprecated Functions **/
@ -871,12 +843,11 @@ class WC_Subscriptions_Change_Payment_Gateway {
/**
* Update the recurring payment method on a subscription order.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
* @deprecated 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
*
* @param string $subscription_key The subscription key.
* @param WC_Order $order The order.
* @param string $new_payment_method The new payment method.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
* @deprecated 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
*/
public static function update_recurring_payment_method( $subscription_key, $order, $new_payment_method ) {
_deprecated_function( __METHOD__, '2.0', __CLASS__ . '::update_payment_method()' );
@ -914,8 +885,6 @@ class WC_Subscriptions_Change_Payment_Gateway {
* For the recurring payment method to be changeable, the subscription must be active, have future (automatic) payments
* and use a payment gateway which allows the subscription to be cancelled.
*
* @deprecated 2.0 Use WC_Subscriptions_Change_Payment_Gateway::can_subscription_be_updated_to_new_payment_method() instead.
*
* @param bool $subscription_can_be_changed Flag of whether the subscription can be changed to
* @param string $new_status_or_meta The status or meta data you want to change th subscription to. Can be 'active', 'on-hold', 'cancelled', 'expired', 'trash', 'deleted', 'failed', 'new-payment-date' or some other value attached to the 'woocommerce_can_subscription_be_changed_to' filter.
* @param object $args Set of values used in @see WC_Subscriptions_Manager::can_subscription_be_changed_to() for determining if a subscription can be changes, include:
@ -941,6 +910,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
* Attach WooCommerce version dependent hooks
*
* @since 1.0.0
*
* @deprecated 1.6.4
*/
public static function attach_dependant_hooks() {

View File

@ -542,9 +542,6 @@ class WC_Subscriptions_Core_Plugin {
update_option( WC_Subscriptions_Admin::$option_prefix . WC_Subscriptions_Email_Notifications::$switch_setting_string, 'yes' );
}
// Setup downloads table.
WC_Subscription_Downloads::install();
update_option( WC_Subscriptions_Admin::$option_prefix . '_is_active', true );
set_transient( $this->get_activation_transient(), true, 60 * 60 );

View File

@ -756,18 +756,7 @@ class WC_Subscriptions_Coupon {
* @return bool Whether the current page is the coupon edit page.
*/
public static function is_coupon_edit_page() {
global $pagenow;
$current_post_type = '';
// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
if ( 'post.php' === $pagenow && isset( $_GET['post'] ) ) {
$current_post_type = get_post_type( $_GET['post'] );
} elseif ( isset( $_GET['post_type'] ) ) {
$current_post_type = $_GET['post_type'];
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
$current_post_type = $_GET['post_type'] ?? ''; // phpcs:ignore
return 'shop_coupon' === $current_post_type;
}

View File

@ -156,11 +156,6 @@ class WC_Subscriptions_Email_Notifications {
switch ( current_action() ) {
case 'woocommerce_scheduled_subscription_customer_notification_renewal':
$subscription = wcs_get_subscription( $subscription_id );
if ( ! $subscription ) {
break;
}
if ( $subscription->get_total() <= 0 ) {
break;
}
@ -172,11 +167,6 @@ class WC_Subscriptions_Email_Notifications {
break;
case 'woocommerce_scheduled_subscription_customer_notification_trial_expiration':
$subscription = wcs_get_subscription( $subscription_id );
if ( ! $subscription ) {
break;
}
if ( $subscription->is_manual() ) {
$notification = $emails['WCS_Email_Customer_Notification_Manual_Trial_Expiration'];
} else {
@ -278,7 +268,7 @@ class WC_Subscriptions_Email_Notifications {
$notification_settings = [
[
'name' => __( 'Customer notifications', 'woocommerce-subscriptions' ),
'name' => __( 'Customer Notifications', 'woocommerce-subscriptions' ),
'type' => 'title',
'id' => WC_Subscriptions_Admin::$option_prefix . '_customer_notifications',
/* translators: Link to WC Settings > Email. */
@ -314,9 +304,7 @@ class WC_Subscriptions_Email_Notifications {
],
];
if ( ! WC_Subscriptions_Admin::insert_setting_after( $settings, WC_Subscriptions_Admin::$option_prefix . '_miscellaneous', $notification_settings, 'multiple_settings', 'sectionend' ) ) {
$settings = array_merge( $settings, $notification_settings );
}
WC_Subscriptions_Admin::insert_setting_after( $settings, WC_Subscriptions_Admin::$option_prefix . '_miscellaneous', $notification_settings, 'multiple_settings', 'sectionend' );
return $settings;
}
}

View File

@ -60,8 +60,6 @@ class WC_Subscriptions_Extend_Store_Endpoint {
// @phpstan-ignore class.notFound
self::$currency_formatter = function_exists( 'woocommerce_store_api_get_formatter' ) ? woocommerce_store_api_get_formatter( 'currency' ) : Package::container()->get( ExtendRestApi::class )->get_formatter( 'currency' );
self::extend_store();
add_action( 'woocommerce_store_api_cart_select_shipping_rate', [ __CLASS__, 'initial_shipment_select_shipping_rate' ], 10, 2 );
}
/**
@ -275,10 +273,9 @@ class WC_Subscriptions_Extend_Store_Endpoint {
// Add extra package data to array.
if ( count( $packages ) ) {
$packages = array_map(
function ( $key, $package, $index ) use ( $cart ) {
function( $key, $package, $index ) use ( $cart ) {
$package['package_id'] = isset( $package['package_id'] ) ? $package['package_id'] : $key;
$package['package_name'] = isset( $package['package_name'] ) ? $package['package_name'] : self::get_shipping_package_name( $package, $cart );
$package['rates'] = apply_filters( 'woocommerce_package_rates', $package['rates'], $package );
return $package;
},
array_keys( $packages ),
@ -334,7 +331,6 @@ class WC_Subscriptions_Extend_Store_Endpoint {
}
$future_subscriptions = array();
$standard_packages = WC()->shipping->get_packages();
if ( ! empty( wc()->cart->recurring_carts ) ) {
foreach ( wc()->cart->recurring_carts as $cart_key => $cart ) {
@ -363,18 +359,7 @@ class WC_Subscriptions_Extend_Store_Endpoint {
'tax_lines' => self::get_tax_lines( $cart ),
)
),
'shipping_rates' => array_values(
array_map(
function ( $package ) use ( $cart, $cart_key, $standard_packages ) {
$shipping_package = self::$schema->get( 'cart-shipping-rate' )->get_item_response( $package );
$shipping_package['match_initial_rates'] = WC_Subscriptions_Cart::package_rates_match_initial_rates( $standard_packages, $package, $cart_key, $cart );
$shipping_package['needs_shipping'] = WC_Subscriptions_Cart::cart_contains_subscriptions_needing_shipping( $cart );
return $shipping_package;
},
$shipping_packages
)
),
'shipping_rates' => array_values( array_map( array( self::$schema->get( 'cart-shipping-rate' ), 'get_item_response' ), $shipping_packages ) ),
);
}
}
@ -382,42 +367,6 @@ class WC_Subscriptions_Extend_Store_Endpoint {
return $future_subscriptions;
}
/**
* Select the initial shipment shipping rate.
*
* @param string $package_id Package ID.
* @param string $rate_id Rate ID.
*/
public static function initial_shipment_select_shipping_rate( $package_id, $rate_id ) {
WC()->cart->calculate_totals();
$standard_packages = WC()->shipping->get_packages();
if ( ! is_numeric( $package_id ) || key( $standard_packages ) !== (int) $package_id ) {
return;
}
foreach ( WC()->cart->recurring_carts as $recurring_cart_key => $recurring_cart ) {
if ( false === $recurring_cart->needs_shipping() || 0 === $recurring_cart->next_payment_date ) {
continue;
}
WC_Subscriptions_Cart::set_calculation_type( 'recurring_total' );
WC_Subscriptions_Cart::set_recurring_cart_key( $recurring_cart_key );
WC_Subscriptions_Cart::set_cached_recurring_cart( $recurring_cart );
foreach ( $recurring_cart->get_shipping_packages() as $recurring_cart_package_key => $recurring_cart_package ) {
if ( WC_Subscriptions_Cart::package_rates_match_initial_rates( $standard_packages, $recurring_cart_package, $recurring_cart_key, $recurring_cart ) ) {
$session_data = wc()->session->get( 'chosen_shipping_methods' ) ? wc()->session->get( 'chosen_shipping_methods' ) : [];
$session_data[ $recurring_cart_package_key ] = $rate_id;
wc()->session->set( 'chosen_shipping_methods', $session_data );
}
}
}
}
/**
* Format sign-up fees.
*

View File

@ -130,10 +130,11 @@ class WC_Subscriptions_Manager {
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.12
*/
public static function process_renewal( $subscription_id, $required_status, $order_note ) {
$subscription = wcs_get_subscription( $subscription_id );
// If the subscription is using manual payments, the gateway isn't active or it manages scheduled payments
if ( $subscription instanceof WC_Subscription && $subscription->has_status( $required_status ) && ( 0 === absint( $subscription->get_total() ) || $subscription->is_manual() || '' === $subscription->get_payment_method() || ! $subscription->payment_method_supports( 'gateway_scheduled_payments' ) ) ) {
if ( ! empty( $subscription ) && $subscription->has_status( $required_status ) && ( 0 == $subscription->get_total() || $subscription->is_manual() || '' == $subscription->get_payment_method() || ! $subscription->payment_method_supports( 'gateway_scheduled_payments' ) ) ) {
// Always put the subscription on hold in case something goes wrong while trying to process renewal
$subscription->update_status( 'on-hold', $order_note );
@ -2074,11 +2075,6 @@ class WC_Subscriptions_Manager {
*/
public static function maybe_process_failed_renewal_for_repair( $subscription_id ) {
$subscription = wcs_get_subscription( $subscription_id );
if ( ! $subscription ) {
return;
}
if ( 'true' === $subscription->get_meta( '_wcs_repaired_2_0_2_needs_failed_payment', true ) ) {
// Always put the subscription on hold in case something goes wrong while trying to process renewal
$subscription->update_status( 'on-hold', _x( 'Subscription renewal payment due:', 'used in order note as reason for why subscription status changed', 'woocommerce-subscriptions' ) );

View File

@ -1045,7 +1045,6 @@ class WC_Subscriptions_Order {
* its own WC_Subscription object.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.5.10
* @deprecated 2.0 Use WC_Subscription::is_editable() instead.
*/
public static function is_order_editable( $is_editable, $order ) {
_deprecated_function( __METHOD__, '2.0', 'WC_Subscription::is_editable()' );
@ -1477,13 +1476,10 @@ class WC_Subscriptions_Order {
/**
* Checks an order to see if it contains a subscription.
*
* @version 1.0.0 Migrated from WooCommerce Subscriptions v1.2
* @since 1.0.0 Migrated from WooCommerce Subscriptions v1.0
* @deprecated 2.0 Use wcs_order_contains_subscription() instead.
*
* @param mixed $order A WC_Order object or the ID of the order which the subscription was purchased in.
*
* @return bool True if the order contains a subscription, otherwise false.
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.0
*/
public static function order_contains_subscription( $order ) {
_deprecated_function( __METHOD__, '2.0', 'wcs_order_contains_subscription( $order )' );
@ -1496,8 +1492,7 @@ class WC_Subscriptions_Order {
*
* With the advent of a separate subscription object in 2.0, this became unnecessary.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
* @deprecated 2.0
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
*/
public static function set_recurring_payment_method( $order_id ) {
_deprecated_function( __METHOD__, '2.0' );
@ -1507,10 +1502,8 @@ class WC_Subscriptions_Order {
* Checks if an order contains an in active subscription and if it does, denies download access
* to files purchased on the order.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.3
* @deprecated 2.0
*
* @return bool False if the order contains a subscription that has expired or is cancelled/on-hold, otherwise, the original value of $download_permitted
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.3
*/
public static function is_download_permitted( $download_permitted, $order ) {
_deprecated_function( __METHOD__, '2.0' );
@ -1523,13 +1516,10 @@ class WC_Subscriptions_Order {
* Deprecated because editing a subscription's values is now done from the Edit Subscription screen and those values
* are stored against a 'shop_subscription' post, not the 'shop_order' used to purchase the subscription.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2.5
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
* @deprecated 2.0
*
* @param WC_Order_Item $item
* @param int $item_id An order_item_id as returned by the insert statement of @see woocommerce_add_order_item()
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2.5
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
* @return void
*/
public static function prefill_order_item_meta( $item, $item_id ) {
@ -1544,10 +1534,7 @@ class WC_Subscriptions_Order {
* are stored against a 'shop_subscription' post, not the 'shop_order' used to purchase the subscription.
*
* Based on the @see woocommerce_calc_line_taxes() function.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2.4
* @deprecated 2.0
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2.4
* @return void
*/
public static function calculate_recurring_line_taxes() {
@ -1563,8 +1550,6 @@ class WC_Subscriptions_Order {
* Deprecated because editing a subscription's values is now done from the Edit Subscription screen and those values
* are stored against a 'shop_subscription' post, not the 'shop_order' used to purchase the subscription.
*
* @deprecated 2.0
*
* @return void
*/
public static function remove_line_tax() {
@ -1580,8 +1565,6 @@ class WC_Subscriptions_Order {
* Deprecated because editing a subscription's values is now done from the Edit Subscription screen and those values
* are stored against a 'shop_subscription' post, not the 'shop_order' used to purchase the subscription.
*
* @deprecated 2.0
*
* @return void
*/
public static function add_line_tax() {
@ -1595,8 +1578,6 @@ class WC_Subscriptions_Order {
* Deprecated because editing a subscription's values is now done from the Edit Subscription screen and those values
* are stored against a 'shop_subscription' post, not the 'shop_order' used to purchase the subscription.
*
* @deprecated 2.0
*
* @param int $post_id The post ID of the shop_order post object.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2.4
* @return void
@ -1613,8 +1594,6 @@ class WC_Subscriptions_Order {
* Deprecated because editing a subscription's values is now done from the Edit Subscription screen and those values
* are stored against a 'shop_subscription' post, not the 'shop_order' used to purchase the subscription.
*
* @deprecated 2.0
*
* @param int $post_id The ID of the post which is the WC_Order object.
* @param Object $post The post object of the order.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.1
@ -1629,8 +1608,6 @@ class WC_Subscriptions_Order {
* Deprecated because editing a subscription's values is now done from the Edit Subscription screen and those values
* are stored against a 'shop_subscription' post, not the 'shop_order' used to purchase the subscription.
*
* @deprecated 2.0
*
* @param int $post_id The ID of the post which is the WC_Order object.
* @param Object $post The post object of the order.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2.4
@ -1643,8 +1620,6 @@ class WC_Subscriptions_Order {
* Checks if a subscription requires manual payment because the payment gateway used to purchase the subscription
* did not support automatic payments at the time of the subscription sign up. Or because we're on a staging site.
*
* @deprecated 2.0 Use WC_Subscription::is_manual() instead.
*
* @param mixed $order A WC_Order object or the ID of the order which the subscription was purchased in.
* @return bool True if the subscription exists and requires manual payments, false if the subscription uses automatic payments (defaults to false for backward compatibility).
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
@ -1670,8 +1645,6 @@ class WC_Subscriptions_Order {
* This may return 0 if there is a free trial period and no sign up fee, otherwise it will be the sum of the sign up
* fee and price per period. This function should be used by payment gateways for the initial payment.
*
* @deprecated 2.0 Use WC_Order::get_total() instead.
*
* @param mixed $order A WC_Order object or the ID of the order which the subscription was purchased in.
* @param int $product_id The ID of the product.
* @return float The total initial amount charged when the subscription product in the order was first purchased, if any.
@ -1690,13 +1663,10 @@ class WC_Subscriptions_Order {
/**
* Returns the recurring amount for an item
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0 Use the value for the item on the subscription object instead.
*
* @param WC_Order $order A WC_Order object
* @param int $product_id The product/post ID of a subscription
*
* @return float The total amount to be charged for each billing period, if any, not including failed payments.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
public static function get_item_recurring_amount( $order, $product_id ) {
_deprecated_function( __METHOD__, '2.0', 'the value for the item on the subscription object rather than the value on the original order. A line item can be deleted from a subscription since Subscriptions v2.0, so even if it exists on an order, it may not exist as a subscription. That means for accurate results, you must use the value on the subscription object' );
@ -1715,11 +1685,9 @@ class WC_Subscriptions_Order {
/**
* Returns the proportion of cart discount that is recurring for the product specified with $product_id
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0 Use the value for the subscription object instead.
*
* @param WC_Order|int $order A WC_Order object or ID of a WC_Order order.
* @param int $product_id The ID of the product.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
public static function get_recurring_discount_cart( $order, $product_id = 0 ) {
_deprecated_function( __METHOD__, '2.0', 'the value for the subscription object rather than the value on the original order. The value is stored against the subscription since Subscriptions v2.0 as an order can be used to create multiple different subscriptions with different discounts, so use the subscription object' );
@ -1748,12 +1716,9 @@ class WC_Subscriptions_Order {
/**
* Returns the proportion of cart discount tax that is recurring for the product specified with $product_id
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0 Use the value for the subscription object instead.
*
* @param WC_Order|int $order A WC_Order object or ID of a WC_Order order.
* @param int $product_id The ID of the product.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
public static function get_recurring_discount_cart_tax( $order, $product_id = 0 ) {
_deprecated_function( __METHOD__, '2.0', 'the value for the subscription object rather than the value on the original order. The value is stored against the subscription since Subscriptions v2.0 as an order can be used to create multiple different subscriptions with different discounts, so use the subscription object' );
@ -1782,19 +1747,17 @@ class WC_Subscriptions_Order {
/**
* Returns the proportion of total discount that is recurring for the product specified with $product_id
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0 Use the value for the subscription object instead.
*
* @param WC_Order|int $order A WC_Order object or ID of a WC_Order order.
* @param int $product_id The ID of the product.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
public static function get_recurring_discount_total( $order, $product_id = 0 ) {
_deprecated_function( __METHOD__, '2.0', 'the value for the subscription object rather than the value on the original order. The value is stored against the subscription since Subscriptions v2.0 as an order can be used to create multiple different subscriptions with different discounts, so use the subscription object' );
$ex_tax = ( 'excl' === get_option( 'woocommerce_tax_display_cart' ) && wcs_get_objects_property( $order, 'display_totals_ex_tax' ) );
$recurring_discount_cart = (float) self::get_recurring_discount_cart( $order );
$recurring_discount_cart_tax = (float) self::get_recurring_discount_cart_tax( $order );
$recurring_discount_cart = (double) self::get_recurring_discount_cart( $order );
$recurring_discount_cart_tax = (double) self::get_recurring_discount_cart_tax( $order );
$recurring_discount_total = 0;
$order_version = wcs_get_objects_property( $order, 'version' );
@ -1831,11 +1794,9 @@ class WC_Subscriptions_Order {
* to recurring payments, and only 1 subscription can be purchased at a time,
* this is equal to @see WC_Order::get_total_tax()
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0 Use the value for the subscription object instead.
*
* @param WC_Order|int $order A WC_Order object or ID of a WC_Order order.
* @param int $product_id The ID of the product.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
public static function get_recurring_shipping_tax_total( $order, $product_id = 0 ) {
_deprecated_function( __METHOD__, '2.0', 'the value for the subscription object rather than the value on the original order. The value is stored against the subscription since Subscriptions v2.0 as an order can be used to create multiple different subscriptions with different amounts, so use the subscription object' );
@ -1866,11 +1827,9 @@ class WC_Subscriptions_Order {
* payments, and only 1 subscription can be purchased at a time, this is
* equal to @see WC_Order::get_total_shipping()
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0 Use the value for the subscription object instead.
*
* @param WC_Order|int $order A WC_Order object or ID of a WC_Order order.
* @param int $product_id The ID of the product.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
public static function get_recurring_shipping_total( $order, $product_id = 0 ) {
_deprecated_function( __METHOD__, '2.0', 'the value for the subscription object rather than the value on the original order. The value is stored against the subscription since Subscriptions v2.0 as an order can be used to create multiple different subscriptions with different amounts, so use the subscription object' );
@ -1899,8 +1858,6 @@ class WC_Subscriptions_Order {
/**
* Return an array of shipping costs within this order.
*
* @deprecated 2.0 Use the shipping for each individual subscription object instead.
*
* @return array
*/
public static function get_recurring_shipping_methods( $order ) {
@ -1918,8 +1875,6 @@ class WC_Subscriptions_Order {
/**
* Returns an array of taxes on an order with their recurring totals.
*
* @deprecated 2.0 Use the taxes for the subscription object instead.
*
* @param WC_Order|int $order A WC_Order object or ID of a WC_Order order.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
@ -1937,11 +1892,10 @@ class WC_Subscriptions_Order {
/**
* Returns the proportion of total tax on an order that is recurring for the product specified with $product_id
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0 Use the value for the subscription object instead.
*
* @param WC_Order|int $order A WC_Order object or ID of a WC_Order order.
* @param int $product_id The ID of the product.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
public static function get_recurring_total_tax( $order, $product_id = 0 ) {
_deprecated_function( __METHOD__, '2.0', 'the value for the subscription object rather than the value on the original order. The value is stored against the subscription since Subscriptions v2.0 as an order can be used to create multiple different subscriptions with different amounts, so use the subscription object' );
@ -1970,11 +1924,9 @@ class WC_Subscriptions_Order {
/**
* Returns the proportion of total before tax on an order that is recurring for the product specified with $product_id
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0 Use the value for the subscription object instead.
*
* @param WC_Order|int $order A WC_Order object or ID of a WC_Order order.
* @param int $product_id The ID of the product.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
public static function get_recurring_total_ex_tax( $order, $product_id = 0 ) {
_deprecated_function( __METHOD__, '2.0', 'the value for the subscription object rather than the value on the original order. The value is stored against the subscription since Subscriptions v2.0 as an order can be used to create multiple different subscriptions with different amounts, so use the subscription object' );
@ -2013,13 +1965,10 @@ class WC_Subscriptions_Order {
/**
* Creates a string representation of the subscription period/term for each item in the cart
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.0
* @deprecated 2.0 Use WC_Subscription::get_formatted_order_total() instead.
*
* @param WC_Order $order A WC_Order object.
* @param mixed $deprecated_price Never used.
* @param mixed $deprecated_sign_up_fee Never used.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.0
*/
public static function get_order_subscription_string( $order, $deprecated_price = '', $deprecated_sign_up_fee = '' ) {
_deprecated_function( __METHOD__, '2.0', 'WC_Subscription::get_formatted_order_total()' );
@ -2034,10 +1983,8 @@ class WC_Subscriptions_Order {
/**
* Returns an array of items in an order which are recurring along with their recurring totals.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0 Use the items on each individual subscription object instead.
*
* @param WC_Order|int $order A WC_Order object or ID of a WC_Order order.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
public static function get_recurring_items( $order ) {
_deprecated_function( __METHOD__, '2.0', 'the items on each individual subscription object (i.e. "shop_subscription")' );
@ -2078,13 +2025,10 @@ class WC_Subscriptions_Order {
/**
* Returns the period (e.g. month) for a each subscription product in an order.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.0
* @deprecated 2.0 Use the billing period for each individual subscription object instead.
*
* @param mixed $order A WC_Order object or the ID of the order which the subscription was purchased in.
* @param int $product_id (optional) The post ID of the subscription WC_Product object purchased in the order. Defaults to the ID of the first product purchased in the order.
*
* @return string A string representation of the period for the subscription, i.e. day, week, month or year.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.0
*/
public static function get_subscription_period( $order, $product_id = 0 ) {
_deprecated_function( __METHOD__, '2.0', 'the billing period for each individual subscription object. Since Subscriptions v2.0, an order can be used to create multiple different subscriptions with different billing schedules, so use the subscription object' );
@ -2534,6 +2478,7 @@ f *
if ( isset( $order->$meta_key ) ) { // WC 2.1+ magic __isset() & __get() methods
$meta_value = $order->$meta_key;
// @phpstan-ignore-next-line
} elseif ( is_array( $order->order_custom_fields ) && isset( $order->order_custom_fields[ '_' . $meta_key ][0] ) && $order->order_custom_fields[ '_' . $meta_key ][0] ) { // < WC 2.1+
$meta_value = maybe_unserialize( $order->order_custom_fields[ '_' . $meta_key ][0] );
} else {

View File

@ -1282,9 +1282,19 @@ class WC_Subscriptions_Product {
}
/**
* Check if the current session has an order awaiting payment for a subscription to a specific product line item.
* If a product is being marked as not purchasable because it is limited and the customer has a subscription,
* but the current request is to resubscribe to the subscription, then mark it as purchasable.
*
* @deprecated 2.1 Use WCS_Limiter::order_awaiting_payment_for_product()
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
* @return bool
*/
public static function is_purchasable( $is_purchasable, $product ) {
_deprecated_function( __METHOD__, '2.1', 'WCS_Limiter::is_purchasable_product' );
return WCS_Limiter::is_purchasable_product( $is_purchasable, $product );
}
/**
* Check if the current session has an order awaiting payment for a subscription to a specific product line item.
*
* @return bool
**/
@ -1331,7 +1341,6 @@ class WC_Subscriptions_Product {
/**
* Returns the sign up fee (including tax) by filtering the products price used in
* @see WC_Product::get_price_including_tax( $qty )
* @deprecated 2.2.0
*
* @return string
*/
@ -1349,7 +1358,6 @@ class WC_Subscriptions_Product {
/**
* Returns the sign up fee (excluding tax) by filtering the products price used in
* @see WC_Product::get_price_excluding_tax( $qty )
* @deprecated 2.2.0
*
* @return string
*/

View File

@ -471,16 +471,11 @@ class WC_Subscriptions_Renewal_Order {
/**
* Check if a given order is a subscription renewal order and optionally, if it is a renewal order of a certain role.
*
* @since 1.2
* @deprecated 2.0 Use wcs_order_contains_resubscribe() and wcs_order_contains_renewal() instead.
*
* @param WC_Order|int $order The WC_Order object or ID of a WC_Order order.
* @param array $args {
* An optional array of name => value flags.
*
* @type string $order_role A specific role to check the order against. Either 'parent' or 'child'. Optional.
* @type bool $via_checkout Indicates whether to check if the renewal order was via the cart/checkout process.
* }
* @param array $args (optional) An array of name => value flags:
* 'order_role' string (optional) A specific role to check the order against. Either 'parent' or 'child'.
* 'via_checkout' Indicates whether to check if the renewal order was via the cart/checkout process.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
*/
public static function is_renewal( $order, $args = array() ) {
@ -565,10 +560,9 @@ class WC_Subscriptions_Renewal_Order {
* @see WC_Subscriptions_Manager::process_subscription_payments_on_order() function would
* never be called. This function makes sure it is called.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0
*
* @param int $order_id The ID of a WC_Order object.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
*/
public static function process_failed_renewal_order_payment( $order_id ) {
_deprecated_function( __METHOD__, '2.0' );
@ -586,10 +580,9 @@ class WC_Subscriptions_Renewal_Order {
/**
* Records manual payment of a renewal order against a subscription.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 2.0
*
* @param int $order_id The ID of a WC_Order object.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
* @deprecated 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
*/
public static function maybe_record_renewal_order_payment( $order_id ) {
_deprecated_function( __METHOD__, '2.0' );
@ -670,8 +663,7 @@ class WC_Subscriptions_Renewal_Order {
/**
* Trigger a hook when a subscription suspended due to a failed renewal payment is reactivated
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.3
* @deprecated 2.0 Use WC_Subscriptions_Renewal_Order::maybe_record_subscription_payment() instead.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.3
*/
public static function trigger_processed_failed_renewal_order_payment_hook( $user_id, $subscription_key ) {
_deprecated_function( __METHOD__, '2.0', __CLASS__ . '::maybe_record_subscription_payment( $order_id, $orders_old_status, $orders_new_status )' );

View File

@ -197,7 +197,7 @@ class WC_Subscriptions_Synchroniser {
public static function add_settings( $settings ) {
$synchronisation_settings = array(
array(
'name' => __( 'Synchronization', 'woocommerce-subscriptions' ),
'name' => __( 'Synchronisation', 'woocommerce-subscriptions' ),
'type' => 'title',
// translators: placeholders are opening and closing link tags
'desc' => sprintf( _x( 'Align subscription renewal to a specific day of the week, month or year. For example, the first day of the month. %1$sLearn more%2$s.', 'used in the general subscription options page', 'woocommerce-subscriptions' ), '<a href="' . esc_url( 'https://woocommerce.com/document/subscriptions/renewal-synchronisation/' ) . '">', '</a>' ),
@ -1356,7 +1356,6 @@ class WC_Subscriptions_Synchroniser {
* are synced and the order total is zero.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.5.17
* @deprecated 2.1.3 Use WC_Subscriptions_Order::maybe_autocomplete_order().
*/
public static function order_autocomplete( $new_order_status, $order_id ) {
_deprecated_function( __METHOD__, '2.1.3', 'WC_Subscriptions_Order::maybe_autocomplete_order' );

View File

@ -5,17 +5,12 @@
* @class WC_Subscriptions_Tracker
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v2.6.4
* @package WooCommerce Subscriptions/Classes
* @category Class
*/
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce_Subscriptions\Internal\Telemetry\Collector;
class WC_Subscriptions_Tracker {
/**
* Handles the collection of additional pieces of data.
*/
private static Collector $telemetry_collector;
/**
* Initialize the Tracker.
@ -23,8 +18,6 @@ class WC_Subscriptions_Tracker {
public static function init() {
// Only add data if Tracker enabled
if ( 'yes' === get_option( 'woocommerce_allow_tracking', 'no' ) ) {
self::$telemetry_collector = new Collector();
self::$telemetry_collector->setup();
add_filter( 'woocommerce_tracker_data', [ __CLASS__, 'add_subscriptions_tracking_data' ], 10, 1 );
}
}
@ -39,11 +32,6 @@ class WC_Subscriptions_Tracker {
$data['extensions']['wc_subscriptions']['settings'] = self::get_subscriptions_options();
$data['extensions']['wc_subscriptions']['subscriptions'] = self::get_subscriptions();
$data['extensions']['wc_subscriptions']['subscription_orders'] = self::get_subscription_orders();
// Insert any additional telemetry that we have been collecting.
$additional_telemetry = self::$telemetry_collector->get_telemetry_data();
$data['extensions']['wc_subscriptions'] = array_merge_recursive( $data['extensions']['wc_subscriptions'], $additional_telemetry );
return $data;
}
@ -53,86 +41,47 @@ class WC_Subscriptions_Tracker {
* @return array Subscriptions options data.
*/
private static function get_subscriptions_options() {
$customer_notifications_offset = get_option( WC_Subscriptions_Admin::$option_prefix . WC_Subscriptions_Email_Notifications::$offset_setting_string, [] );
return [
// Staging and live site
'wc_subscriptions_staging' => WCS_Staging::is_duplicate_site() ? 'staging' : 'live',
'wc_subscriptions_live_url' => esc_url( WCS_Staging::get_site_url_from_source( 'subscriptions_install' ) ),
// Button text
// Add to Cart Button Text
// Button text, roles
'add_to_cart_button_text' => get_option( WC_Subscriptions_Admin::$option_prefix . '_add_to_cart_button_text' ),
// Place Order Button Text
'order_button_text' => get_option( WC_Subscriptions_Admin::$option_prefix . '_order_button_text' ),
// Roles
// Subscriber Default Role
'subscriber_role' => get_option( WC_Subscriptions_Admin::$option_prefix . '_subscriber_role' ),
// Inactive Subscriber Role
'cancelled_role' => get_option( WC_Subscriptions_Admin::$option_prefix . '_cancelled_role' ),
// Renewals
// Accept Manual Renewals
'accept_manual_renewals' => get_option( WC_Subscriptions_Admin::$option_prefix . '_accept_manual_renewals' ),
// Turn off Automatic Payments
'turn_off_automatic_payments' => 'no' === get_option( WC_Subscriptions_Admin::$option_prefix . '_accept_manual_renewals' ) ? 'none' : get_option( WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'none' ),
// Auto Renewal Toggle
'enable_auto_renewal_toggle' => get_option( WC_Subscriptions_Admin::$option_prefix . '_enable_auto_renewal_toggle' ),
// Early renewal
// Accept Early Renewal Payments
'enable_early_renewal' => get_option( WC_Subscriptions_Admin::$option_prefix . '_enable_early_renewal' ),
// Accept Early Renewal Payments via a Modal
'enable_early_renewal_via_modal' => 'no' === get_option( WC_Subscriptions_Admin::$option_prefix . '_enable_early_renewal' ) ? 'none' : get_option( WC_Subscriptions_Admin::$option_prefix . '_enable_early_renewal_via_modal', 'none' ),
// Switching
// Between Subscription Variations and Between Grouped Subscriptions are condensed into this setting.
'allow_switching' => get_option( WC_Subscriptions_Admin::$option_prefix . '_allow_switching' ),
// Prorate Recurring Payment
'apportion_recurring_price' => get_option( WC_Subscriptions_Admin::$option_prefix . '_apportion_recurring_price', 'none' ),
// Prorate Sign up Fee
'apportion_sign_up_fee' => get_option( WC_Subscriptions_Admin::$option_prefix . '_apportion_sign_up_fee', 'none' ),
// Prorate Subscription Length
'apportion_length' => get_option( WC_Subscriptions_Admin::$option_prefix . '_apportion_length', 'none' ),
// Switch Button Text
'switch_button_text' => get_option( WC_Subscriptions_Admin::$option_prefix . '_switch_button_text', 'none' ),
// Gifting
// Enable gifting
'gifting_enable_gifting' => get_option( WC_Subscriptions_Admin::$option_prefix . '_gifting_enable_gifting' ),
'gifting_default_option' => get_option( WC_Subscriptions_Admin::$option_prefix . '_gifting_default_option' ),
// Gifting Checkbox Text
'gifting_gifting_checkbox_text' => get_option( WC_Subscriptions_Admin::$option_prefix . '_gifting_gifting_checkbox_text' ),
// Downloadable Products
'gifting_downloadable_products' => get_option( WC_Subscriptions_Admin::$option_prefix . '_gifting_downloadable_products' ),
// Synchronization
// Synchronise renewals
'sync_payments' => get_option( WC_Subscriptions_Admin::$option_prefix . '_sync_payments' ),
// Prorate First Renewal
'prorate_synced_payments' => $prorate_synced_payments = ( 'no' === get_option( WC_Subscriptions_Admin::$option_prefix . '_sync_payments' ) ? 'none' : get_option( WC_Subscriptions_Admin::$option_prefix . '_prorate_synced_payments', 'none' ) ),
// Sign-up grace period
'days_no_fee' => 'recurring' === $prorate_synced_payments ? get_option( WC_Subscriptions_Admin::$option_prefix . '_days_no_fee', 'none' ) : 'none',
// Miscellaneous
// Customer Suspensions
'max_customer_suspensions' => get_option( WC_Subscriptions_Admin::$option_prefix . '_max_customer_suspensions' ),
// Mixed Checkout
'multiple_purchase' => get_option( WC_Subscriptions_Admin::$option_prefix . '_multiple_purchase' ),
// $0 Initial Checkout
'allow_zero_initial_order_without_payment_method' => get_option( WC_Subscriptions_Admin::$option_prefix . '_zero_initial_payment_requires_payment' ),
// Drip Downloadable Content
'drip_downloadable_content_on_renewal' => get_option( WC_Subscriptions_Admin::$option_prefix . '_drip_downloadable_content_on_renewal' ),
// Retry Failed Payments
'enable_retry' => get_option( WC_Subscriptions_Admin::$option_prefix . '_enable_retry' ),
// Notifications
// Enable Reminders
'enable_notification_reminders' => get_option( WC_Subscriptions_Admin::$option_prefix . WC_Subscriptions_Email_Notifications::$switch_setting_string ),
// Reminder Timing
'customer_notifications_offset_number' => $customer_notifications_offset['number'] ?? 'none',
'customer_notifications_offset_unit' => $customer_notifications_offset['unit'] ?? 'none',
'enable_notification_reminders' => get_option( WC_Subscriptions_Admin::$option_prefix . WC_Subscriptions_Email_Notifications::$switch_setting_string, 'no' ),
];
}
@ -170,21 +119,80 @@ class WC_Subscriptions_Tracker {
* @return array Subscription order counts and totals by type (initial, switch, renewal, resubscribe). Values are returned as strings.
*/
private static function get_subscription_orders() {
$order_totals = array();
$order_totals = [];
$relation_types = [
'switch',
'renewal',
'resubscribe',
];
// Get the subtotal and count for all subscription types in one query
$counts_and_totals = self::get_order_count_and_total_by_meta_key();
// Get the subtotal and count for each subscription type.
foreach ( $relation_types as $relation_type ) {
// Get orders with the given relation type.
$relation_orders = wcs_get_orders_with_meta_query(
[
'type' => 'shop_order',
'status' => [ 'wc-completed', 'wc-processing', 'wc-refunded' ],
'limit' => -1,
'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
[
'key' => '_subscription_' . $relation_type,
'compare' => 'EXISTS',
],
],
]
);
foreach ( $counts_and_totals as $type => $data ) {
$order_totals[ $type . '_gross' ] = $data['total'];
$order_totals[ $type . '_count' ] = $data['count'];
// Sum the totals and count the orders.
$count = count( $relation_orders );
$total = array_reduce(
$relation_orders,
function( $total, $order ) {
return $total + $order->get_total();
},
0
);
$order_totals[ $relation_type . '_gross' ] = $total;
$order_totals[ $relation_type . '_count' ] = $count;
}
// Get initial orders (orders without switch, renewal, or resubscribe meta keys).
$count_and_total = self::get_initial_order_count_and_total();
// Finally, get the initial revenue and count.
// Get the orders for all initial subscription orders (no switch, renewal or resubscribe meta key).
$initial_subscription_orders = wcs_get_orders_with_meta_query(
[
'type' => 'shop_order',
'status' => [ 'wc-completed', 'wc-processing', 'wc-refunded' ],
'limit' => -1,
'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
[
'key' => '_subscription_switch',
'compare' => 'NOT EXISTS',
],
[
'key' => '_subscription_renewal',
'compare' => 'NOT EXISTS',
],
[
'key' => '_subscription_resubscribe',
'compare' => 'NOT EXISTS',
],
],
]
);
$order_totals['initial_gross'] = $count_and_total['total'];
$order_totals['initial_count'] = $count_and_total['count'];
// Sum the totals and count the orders.
$initial_order_count = count( $initial_subscription_orders );
$initial_order_total = array_reduce(
$initial_subscription_orders,
function( $total, $order ) {
return $total + $order->get_total();
},
0
);
$order_totals['initial_gross'] = $initial_order_total;
$order_totals['initial_count'] = $initial_order_count;
// Ensure all values are strings.
$order_totals = array_map( 'strval', $order_totals );
@ -192,149 +200,6 @@ class WC_Subscriptions_Tracker {
return $order_totals;
}
/**
* Gets order count and total for subscription-related orders.
*
* @return array Array with counts and totals for switch, renewal, and resubscribe orders.
*/
private static function get_order_count_and_total_by_meta_key() {
global $wpdb;
$order_statuses = array( 'wc-completed', 'wc-refunded' );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
if ( wcs_is_custom_order_tables_usage_enabled() ) {
// HPOS: Use wc_orders and wc_orders_meta tables.
$orders_table = $wpdb->prefix . 'wc_orders';
$meta_table = $wpdb->prefix . 'wc_orders_meta';
$query = $wpdb->prepare(
"SELECT
order_relation.meta_key as 'type',
SUM( orders.total_amount ) AS 'total',
COUNT( orders.id ) as 'count'
FROM {$orders_table} AS orders
RIGHT JOIN {$meta_table} AS order_relation ON order_relation.order_id = orders.id
WHERE order_relation.meta_key IN ( '_subscription_switch', '_subscription_renewal', '_subscription_resubscribe' )
AND orders.status in (%s, %s)
AND orders.type = 'shop_order'
GROUP BY order_relation.meta_key",
$order_statuses
);
} else {
// CPT: Use posts and postmeta tables.
$query = $wpdb->prepare(
"SELECT
order_relation.meta_key as 'type',
SUM( order_total.meta_value ) AS 'total',
COUNT( orders.ID ) as 'count'
FROM {$wpdb->prefix}posts AS orders
RIGHT JOIN {$wpdb->prefix}postmeta AS order_relation ON order_relation.post_id = orders.ID
RIGHT JOIN {$wpdb->prefix}postmeta AS order_total ON order_total.post_id = orders.ID
WHERE order_relation.meta_key IN ( '_subscription_switch', '_subscription_renewal', '_subscription_resubscribe' )
AND orders.post_status in (%s, %s)
AND order_total.meta_key = '_order_total'
GROUP BY order_relation.meta_key",
$order_statuses
);
}
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
$results = $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$totals = array(
'switch' => array(
'count' => 0,
'total' => 0.0,
),
'renewal' => array(
'count' => 0,
'total' => 0.0,
),
'resubscribe' => array(
'count' => 0,
'total' => 0.0,
),
);
if ( $results ) {
foreach ( $results as $result ) {
$type = str_replace( '_subscription_', '', $result['type'] );
if ( isset( $totals[ $type ] ) ) {
$totals[ $type ] = array(
'count' => (int) $result['count'],
'total' => (float) $result['total'],
);
}
}
}
// Log if any type has no data
foreach ( $totals as $type => $data ) {
if ( 0 === $data['count'] && 0.0 === $data['total'] ) {
wc_get_logger()->warning( "WC_Subscriptions_Tracker::get_order_count_and_total_by_meta_key() returned 0 count and total for {$type} orders" );
}
}
return $totals;
}
/**
* Gets count and total for initial orders (orders without subscription relation meta keys).
*
* @return array Array with 'count' and 'total' keys.
*/
private static function get_initial_order_count_and_total() {
global $wpdb;
$order_statuses = array( 'wc-completed', 'wc-refunded' );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
if ( wcs_is_custom_order_tables_usage_enabled() ) {
// HPOS: Use wc_orders table
$orders_table = $wpdb->prefix . 'wc_orders';
$query = $wpdb->prepare(
"SELECT
SUM( orders.total_amount ) AS 'total', COUNT( DISTINCT orders.id ) as 'count'
FROM {$orders_table} AS orders
RIGHT JOIN {$orders_table} AS subscriptions ON subscriptions.parent_order_id = orders.id
WHERE orders.status in ( %s, %s )
AND subscriptions.type = 'shop_subscription'
AND orders.type = 'shop_order'",
$order_statuses
);
} else {
// CPT: Use posts and postmeta tables.
$query = $wpdb->prepare(
"SELECT
SUM( order_total.meta_value ) AS 'total', COUNT( * ) as 'count'
FROM {$wpdb->posts} AS orders
RIGHT JOIN {$wpdb->posts} AS subscriptions ON subscriptions.post_parent = orders.ID
RIGHT JOIN {$wpdb->postmeta} AS order_total ON order_total.post_id = orders.ID
WHERE orders.post_status in ( %s, %s )
AND subscriptions.post_type = 'shop_subscription'
AND orders.post_type = 'shop_order'
AND order_total.meta_key = '_order_total'",
$order_statuses
);
}
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
$result = $wpdb->get_row( $query, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$totals = array(
'count' => (int) ( $result ? $result['count'] : 0 ),
'total' => (float) ( $result ? $result['total'] : 0 ),
);
if ( 0 === $totals['count'] && 0.0 === $totals['total'] ) {
wc_get_logger()->warning( 'WC_Subscriptions_Tracker::get_initial_order_count_and_total() returned 0 count and total' );
}
return $totals;
}
/**
* Gets first and last subscription created dates.
*

View File

@ -65,8 +65,6 @@ class WCS_Cached_Data_Manager extends WCS_Cache_Manager {
/**
* Clearing cache when a post is deleted
*
* @deprecated 2.3.0
*
* @param int $post_id The ID of a post
* @param WP_Post $post The post object (on certain hooks).
*/
@ -166,8 +164,6 @@ class WCS_Cached_Data_Manager extends WCS_Cache_Manager {
/**
* If the log is bigger than a threshold it will be
* truncated to 0 bytes.
*
* @deprecated 6.0.0
*/
public static function cleanup_logs() {
wcs_deprecated_function( __METHOD__, '6.0.0' );
@ -199,8 +195,7 @@ class WCS_Cached_Data_Manager extends WCS_Cache_Manager {
/**
* Check once each week if the log file has exceeded the limits.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.9
* @deprecated 6.0.0
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.9
*/
public function initialize_cron_check_size() {
wcs_deprecated_function( __METHOD__, '6.0.0' );
@ -242,12 +237,7 @@ class WCS_Cached_Data_Manager extends WCS_Cache_Manager {
protected function purge_subscription_user_cache( $subscription_id ) {
wcs_deprecated_argument( __METHOD__, '2.3.0', sprintf( __( 'Customer subscription caching is now handled by %1$s and %2$s.', 'woocommerce-subscriptions' ), 'WCS_Customer_Store_Cached_CPT', 'WCS_Post_Meta_Cache_Manager' ) ); // phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment
$subscription = wcs_get_subscription( $subscription_id );
if ( ! $subscription ) {
return;
}
$subscription = wcs_get_subscription( $subscription_id );
$subscription_user_id = $subscription->get_user_id();
$this->log( sprintf(
'Clearing cache for user ID %1$s on %2$s hook.',

View File

@ -604,10 +604,6 @@ class WCS_Cart_Renewal {
$subscription = wcs_get_subscription( $cart_renewal_item[ $this->cart_item_key ]['subscription_id'] );
if ( ! $subscription ) {
return $update_customer_data;
}
$billing_address = array();
if ( $checkout_object->checkout_fields['billing'] ) {
foreach ( array_keys( $checkout_object->checkout_fields['billing'] ) as $field ) {
@ -631,6 +627,19 @@ class WCS_Cart_Renewal {
return $update_customer_data;
}
/**
* If a product is being marked as not purchasable because it is limited and the customer has a subscription,
* but the current request is to resubscribe to the subscription, then mark it as purchasable.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
* @return bool
*/
public function is_purchasable( $is_purchasable, $product ) {
_deprecated_function( __METHOD__, '2.1', 'WCS_Limiter::is_purchasable_renewal' );
return WCS_Limiter::is_purchasable_renewal( $is_purchasable, $product );
}
/**
* Flag payment of manual renewal orders via an extra URL param.
*
@ -1186,12 +1195,7 @@ class WCS_Cart_Renewal {
$cart_renewal_item = $this->cart_contains();
if ( false !== $cart_renewal_item ) {
$subscription = wcs_get_subscription( $cart_renewal_item[ $this->cart_item_key ]['subscription_id'] );
if ( ! $subscription ) {
return;
}
$subscription = wcs_get_subscription( $cart_renewal_item[ $this->cart_item_key ]['subscription_id'] );
$subscription_updated = false;
foreach ( [ 'billing', 'shipping' ] as $address_type ) {
@ -1227,10 +1231,6 @@ class WCS_Cart_Renewal {
if ( false !== $cart_renewal_item ) {
$subscription = wcs_get_subscription( $cart_renewal_item[ $this->cart_item_key ]['subscription_id'] );
if ( ! $subscription ) {
return;
}
// Billing address is a required field.
foreach ( $request['billing_address'] as $key => $value ) {
if ( is_callable( [ $customer, "set_billing_$key" ] ) ) {

View File

@ -186,6 +186,19 @@ class WCS_Cart_Resubscribe extends WCS_Cart_Renewal {
return $cart_item_session_data;
}
/**
* If a product is being marked as not purchasable because it is limited and the customer has a subscription,
* but the current request is to resubscribe to the subscription, then mark it as purchasable.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
* @return bool
*/
public function is_purchasable( $is_purchasable, $product ) {
_deprecated_function( __METHOD__, '2.1', 'WCS_Limiter::is_purchasable_renewal' );
return WCS_Limiter::is_purchasable_renewal( $is_purchasable, $product );
}
/**
* Checks the cart to see if it contains a subscription resubscribe item.
*

View File

@ -121,6 +121,7 @@ class WCS_Failed_Scheduled_Action_Manager {
// Log any exceptions caught by the exception listener in action logs.
if ( ! empty( $context['exceptions'] ) ) {
foreach ( $context['exceptions'] as $exception_message ) {
// @phpstan-ignore-next-line
ActionScheduler_Logger::instance()->log( $action_id, $exception_message );
}
}

View File

@ -23,6 +23,8 @@ class WCS_Limiter {
if ( wcs_is_frontend_request() || wcs_is_checkout_blocks_api_request() ) {
add_filter( 'woocommerce_subscription_is_purchasable', __CLASS__ . '::is_purchasable_switch', 12, 2 );
add_filter( 'woocommerce_subscription_variation_is_purchasable', __CLASS__ . '::is_purchasable_switch', 12, 2 );
add_filter( 'woocommerce_subscription_is_purchasable', __CLASS__ . '::is_purchasable_renewal', 12, 2 );
add_filter( 'woocommerce_subscription_variation_is_purchasable', __CLASS__ . '::is_purchasable_renewal', 12, 2 );
add_filter( 'woocommerce_valid_order_statuses_for_order_again', array( __CLASS__, 'filter_order_again_statuses_for_limited_subscriptions' ) );
}
}
@ -57,43 +59,6 @@ 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.
*
@ -104,82 +69,36 @@ class WCS_Limiter {
* @return bool
*/
public static function is_purchasable( $purchasable, $product ) {
// 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();
}
}
// 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;
switch ( $product->get_type() ) {
case 'subscription':
case 'variable-subscription':
if ( true === $purchasable && false === self::is_purchasable_product( $purchasable, $product ) ) {
$purchasable = false;
}
}
break;
case 'subscription_variation':
$variable_product = wc_get_product( $product->get_parent_id() );
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;
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;
}
}
}
}
}
} 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();
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce validation is not required for this context.
$is_resubscribe = ! empty( $_GET['resubscribe'] ) || false !== $resubscribe_cart_item || false !== self::session_contains_resubscribe( $product );
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce validation is not required for this context.
$is_renewal = isset( $_GET['subscription_renewal'] ) || wcs_cart_contains_renewal() || self::session_contains_renewal( $product );
// Allows the product to be resubscribed but not purchased again.
if ( ! $is_resubscribe && ! $is_renewal ) {
$purchasable = false;
}
}
break;
}
return $purchasable;
}
/**
* If a product is limited and the customer already has a subscription, mark it as not purchasable.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.1, Moved from WC_Subscriptions_Product
* @deprecated 8.0.0 Use WCS_Limiter::is_product_limited().
*
* @return bool
*/
public static function is_purchasable_product( $is_purchasable, $product ) {
_deprecated_function( __METHOD__, '8.0.0', 'WCS_Limiter::is_product_limited' );
return self::is_product_limited( $is_purchasable, $product );
}
/**
* If a product is limited and the customer already has a subscription, mark it as not purchasable.
@ -187,7 +106,7 @@ class WCS_Limiter {
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.1, Moved from WC_Subscriptions_Product
* @return bool
*/
public static function is_product_limited( $is_purchasable, $product ) {
public static function is_purchasable_product( $is_purchasable, $product ) {
// Set up cache
if ( ! isset( self::$is_purchasable_cache[ $product->get_id() ] ) ) {
@ -274,15 +193,37 @@ class WCS_Limiter {
/**
* Determines whether a product is purchasable based on whether the cart is to resubscribe or renew.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.1, Combines WCS_Cart_Renewal::is_purchasable and WCS_Cart_Resubscribe::is_purchasable
* @deprecated 1.0.0 Use WCS_Limiter::is_product_limited().
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.1, Combines WCS_Cart_Renewal::is_purchasable and WCS_Cart_Resubscribe::is_purchasable
* @return bool
*/
public static function is_purchasable_renewal( $is_purchasable, $product ) {
_deprecated_function( __METHOD__, '8.0.0', 'WCS_Limiter::is_product_limited' );
if ( false === $is_purchasable && false === self::is_purchasable_product( $is_purchasable, $product ) ) {
$resubscribe_cart_item = wcs_cart_contains_resubscribe();
return ! self::is_product_limited( $is_purchasable, $product );
// Resubscribe logic
if ( isset( $_GET['resubscribe'] ) || false !== $resubscribe_cart_item ) {
$subscription_id = ( isset( $_GET['resubscribe'] ) ) ? absint( $_GET['resubscribe'] ) : $resubscribe_cart_item['subscription_resubscribe']['subscription_id'];
$subscription = wcs_get_subscription( $subscription_id );
if ( false != $subscription && $subscription->has_product( $product->get_id() ) && wcs_can_user_resubscribe_to( $subscription ) ) {
$is_purchasable = true;
}
// Renewal logic
} elseif ( isset( $_GET['subscription_renewal'] ) || wcs_cart_contains_renewal() ) {
$is_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 ( $product->get_id() == $cart_item['product_id'] && ( isset( $cart_item['subscription_renewal'] ) || isset( $cart_item['subscription_resubscribe'] ) ) ) {
$is_purchasable = true;
break;
}
}
}
}
return $is_purchasable;
}
/**
@ -336,73 +277,12 @@ 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 8.3.0 - Migrated from WooCommerce Subscriptions v3.0.2
* @since 1.0.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.

View File

@ -51,13 +51,6 @@ class WCS_Modal {
*/
private $actions = array();
/**
* A unique ID for the modal to use in HTML ID attribute.
*
* @var string
*/
private $id = '';
/**
* Registers the scripts and stylesheets needed to display the modals.
*
@ -111,7 +104,6 @@ class WCS_Modal {
$this->trigger = $trigger;
$this->heading = $heading;
$this->actions = $actions;
$this->id = wp_unique_id( 'wcs-modal-' );
// Allow callers to provide the callback without any parameters. Assuming the content provided is the callback.
if ( 'callback' === $this->content_type && ! isset( $content['parameters'] ) ) {
@ -237,30 +229,6 @@ class WCS_Modal {
return $this->trigger;
}
/**
* Sets the modal's unique ID.
*
* This is used for the actual HTML ID attribute, and so should follow the normal CSS identifier rules.
*
* @since 8.2.0
*
* @param string $id The modal's unique ID.
*/
public function set_id( $id ) {
$this->id = $id;
}
/**
* Returns the modal's unique ID.
*
* @since 8.2.0
*
* @return string The modal's unique ID.
*/
public function get_id() {
return $this->id;
}
/**
* Returns a flattened string of HTML element attributes from an array of attributes and values.
*

View File

@ -187,20 +187,14 @@ class WCS_My_Account_Payment_Methods {
return;
}
$notice = sprintf(
// translators: 1: token display name, 2: opening link tag, 4: closing link tag, 3: opening link tag.
esc_html__( 'Would you like to update your subscriptions to use this new payment method - %1$s?%2$sYes%4$s | %3$sNo%4$s', 'woocommerce-subscriptions' ),
// translators: 1: token display name, 2: opening link tag, 4: closing link tag, 3: opening link tag.
$notice = sprintf( esc_html__( 'Would you like to update your subscriptions to use this new payment method - %1$s?%2$sYes%4$s | %3$sNo%4$s', 'woocommerce-subscriptions' ),
$default_token->get_display_name(),
'<br><a href="' . esc_url(
add_query_arg(
array(
'update-subscription-tokens' => 'true',
'token-id' => $default_token_id,
'_wcsnonce' => wp_create_nonce( 'wcs-update-subscription-tokens' ),
),
wc_get_account_endpoint_url( 'payment-methods' )
)
) . '"><strong>',
'</br><a href="' . esc_url( add_query_arg( array(
'update-subscription-tokens' => 'true',
'token-id' => $default_token_id,
'_wcsnonce' => wp_create_nonce( 'wcs-update-subscription-tokens' ),
), wc_get_account_endpoint_url( 'payment-methods' ) ) ) . '"><strong>',
'<a href=""><strong>',
'</strong></a>'
);

View File

@ -92,9 +92,11 @@ class WCS_Object_Data_Cache_Manager extends WCS_Post_Meta_Cache_Manager {
}
$force_all_fields = 'all_fields' === $generate_type;
$changes = $subscription->get_changes();
$base_data = $subscription->get_base_data();
$meta_data = $subscription->get_meta_data();
// @phpstan-ignore-next-line
$changes = $subscription->get_changes();
$base_data = $subscription->get_base_data();
// @phpstan-ignore-next-line
$meta_data = $subscription->get_meta_data();
// Deleted meta won't be included in the changes, so we need to fetch the previous value via the raw meta data.
$data_store = $subscription->get_data_store();
@ -142,6 +144,7 @@ class WCS_Object_Data_Cache_Manager extends WCS_Post_Meta_Cache_Manager {
} elseif ( $meta->get_changes() ) {
// If the value is being updated.
$this->object_changes[ $subscription->get_id() ][ $data_key ] = [
// @phpstan-ignore-next-line
'new' => $meta->value,
'previous' => isset( $previous_meta['value'] ) ? $previous_meta['value'] : null,
'type' => 'update',
@ -149,6 +152,7 @@ class WCS_Object_Data_Cache_Manager extends WCS_Post_Meta_Cache_Manager {
} elseif ( $force_all_fields ) {
// If we're forcing all fields to be recorded.
$this->object_changes[ $subscription->get_id() ][ $data_key ] = [
// @phpstan-ignore-next-line
'new' => $meta->value,
'type' => 'add',
];

View File

@ -13,13 +13,12 @@ class WCS_Query extends WC_Query {
add_filter( 'the_title', array( $this, 'change_endpoint_title' ), 11, 1 );
add_filter( 'woocommerce_get_query_vars', array( $this, 'add_wcs_query_vars' ) );
if ( ! is_admin() ) {
add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 );
add_action( 'parse_request', array( $this, 'parse_request' ), 0 );
add_action( 'pre_get_posts', array( $this, 'maybe_redirect_payment_methods' ) );
add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ), 11 );
add_filter( 'woocommerce_get_query_vars', array( $this, 'add_wcs_query_vars' ) );
// Inserting your new tab/page into the My Account page.
add_filter( 'woocommerce_account_menu_items', array( $this, 'add_menu_items' ) );

View File

@ -280,7 +280,7 @@ class WCS_Staging {
if ( 'subscriptions_install' === $source ) {
$site_url = self::get_live_site_url();
} elseif ( ! is_multisite() && defined( 'WP_SITEURL' ) ) {
$site_url = WP_SITEURL; // @phpstan-ignore phpstanWP.wpConstant.fetch (Using constant for performance and staging-specific behavior)
$site_url = WP_SITEURL;
} else {
$site_url = get_site_url();
}

View File

@ -49,10 +49,6 @@ class WC_Subscriptions_Email_Preview {
case 'WCS_Email_Customer_Notification_Auto_Renewal':
$email->set_object( $this->get_dummy_subscription() );
break;
case 'WCSG_Email_Recipient_New_Initial_Order':
$email->set_object( $this->get_dummy_subscription() );
$email->subscriptions = [ $this->get_dummy_subscription() ];
break;
case 'WCS_Email_Customer_Payment_Retry':
case 'WCS_Email_Payment_Retry':
$email->retry = $this->get_dummy_retry( $email->object );
@ -195,7 +191,8 @@ class WC_Subscriptions_Email_Preview {
* @return bool Whether the email being previewed is a subscription email.
*/
private function is_subscription_email() {
return isset( apply_filters( 'wcs_email_classes', array_merge( WC_Subscriptions_Email::$email_classes, WC_Subscriptions_Email_Notifications::$email_classes ) )[ $this->email_type ] )
return isset( WC_Subscriptions_Email::$email_classes[ $this->email_type ] )
|| isset( WC_Subscriptions_Email_Notifications::$email_classes[ $this->email_type ] )
|| in_array( $this->email_type, [ 'WCS_Email_Customer_Payment_Retry', 'WCS_Email_Payment_Retry' ], true );
}

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v2.1
* @package WooCommerce_Subscriptions/Classes/Emails
* @author Prospress
* @extends WC_Email
*/
class WCS_Email_Cancelled_Subscription extends WC_Email {

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v2.0.0
* @package WooCommerce/Classes/Emails
* @author Prospress
* @extends WC_Email
*/
class WCS_Email_Completed_Renewal_Order extends WC_Email_Customer_Completed_Order {

View File

@ -12,6 +12,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v2.0.0
* @package WooCommerce/Classes/Emails
* @author Prospress
* @extends WC_Email
*/
class WCS_Email_Completed_Switch_Order extends WC_Email_Customer_Completed_Order {

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @class WCS_Email_Customer_Notification_Auto_Renewal
* @version 1.0.0
* @package WooCommerce_Subscriptions/Classes/Emails
* @extends WC_Email
*/
class WCS_Email_Customer_Notification_Auto_Renewal extends WCS_Email_Customer_Notification {

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @class WCS_Email_Customer_Notification_Free_Trial_Expiry
* @version 1.0.0
* @package WooCommerce_Subscriptions/Classes/Emails
* @extends WC_Email
*/
class WCS_Email_Customer_Notification_Auto_Trial_Expiration extends WCS_Email_Customer_Notification {

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @class WCS_Email_Customer_Notification_Manual_Renewal
* @version 1.0.0
* @package WooCommerce_Subscriptions/Classes/Emails
* @extends WC_Email
*/
class WCS_Email_Customer_Notification_Manual_Renewal extends WCS_Email_Customer_Notification {

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @class WCS_Email_Customer_Notification_Free_Trial_Expiry
* @version 1.0.0
* @package WooCommerce_Subscriptions/Classes/Emails
* @extends WC_Email
*/
class WCS_Email_Customer_Notification_Manual_Trial_Expiration extends WCS_Email_Customer_Notification {

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @class WCS_Email_Customer_Notification_Subscription_Expiring
* @version 1.0.0
* @package WooCommerce_Subscriptions/Classes/Emails
* @extends WC_Email
*/
class WCS_Email_Customer_Notification_Subscription_Expiration extends WCS_Email_Customer_Notification {

View File

@ -10,6 +10,7 @@ defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v3.0.0
* @package WooCommerce_Subscriptions/Includes/Emails
* @author WooCommerce.
* @extends WC_Email_Customer_On_Hold_Order
*/
class WCS_Email_Customer_On_Hold_Renewal_Order extends WC_Email_Customer_On_Hold_Order {
@ -21,7 +22,8 @@ class WCS_Email_Customer_On_Hold_Renewal_Order extends WC_Email_Customer_On_Hold
$this->customer_email = true;
$this->title = __( 'On-hold Renewal Order', 'woocommerce-subscriptions' );
$this->description = __( 'This is an order notification sent to customers containing order details after a renewal order is placed on-hold.', 'woocommerce-subscriptions' );
$this->subject = __( 'Your {site_title} renewal order has been received!', 'woocommerce-subscriptions' );
$this->heading = __( 'Thank you for your renewal order', 'woocommerce-subscriptions' );
$this->template_html = 'emails/customer-on-hold-renewal-order.php';
$this->template_plain = 'emails/plain/customer-on-hold-renewal-order.php';
$this->template_base = WC_Subscriptions_Plugin::instance()->get_plugin_directory( 'templates/' );
@ -46,7 +48,7 @@ class WCS_Email_Customer_On_Hold_Renewal_Order extends WC_Email_Customer_On_Hold
* @return string
*/
public function get_default_subject() {
return __( 'Your {site_title} renewal order has been received!', 'woocommerce-subscriptions' );
return $this->subject;
}
/**
@ -56,7 +58,7 @@ class WCS_Email_Customer_On_Hold_Renewal_Order extends WC_Email_Customer_On_Hold
* @return string
*/
public function get_default_heading() {
return __( 'Thank you for your renewal order', 'woocommerce-subscriptions' );
return $this->heading;
}
/**

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
* @package WooCommerce_Subscriptions/Includes/Emails
* @author Prospress
* @extends WC_Email_Customer_Invoice
*/
class WCS_Email_Customer_Renewal_Invoice extends WC_Email_Customer_Invoice {
@ -44,6 +45,9 @@ class WCS_Email_Customer_Renewal_Invoice extends WC_Email_Customer_Invoice {
$this->template_plain = 'emails/plain/customer-renewal-invoice.php';
$this->template_base = WC_Subscriptions_Plugin::instance()->get_plugin_directory( 'templates/' );
$this->subject = __( 'Invoice for renewal order {order_number} from {order_date}', 'woocommerce-subscriptions' );
$this->heading = __( 'Invoice for renewal order {order_number}', 'woocommerce-subscriptions' );
// Triggers for this email
add_action( 'woocommerce_generated_manual_renewal_order_renewal_notification', array( $this, 'trigger' ) );
add_action( 'woocommerce_order_status_failed_renewal_notification', array( $this, 'trigger' ) );
@ -60,7 +64,7 @@ class WCS_Email_Customer_Renewal_Invoice extends WC_Email_Customer_Invoice {
* @return string
*/
public function get_default_subject( $paid = false ) {
return __( 'Invoice for renewal order {order_number} from {order_date}', 'woocommerce-subscriptions' );
return $this->subject;
}
/**
@ -71,7 +75,7 @@ class WCS_Email_Customer_Renewal_Invoice extends WC_Email_Customer_Invoice {
* @return string
*/
public function get_default_heading( $paid = false ) {
return __( 'Invoice for renewal order {order_number}', 'woocommerce-subscriptions' );
return $this->heading;
}
/**
@ -124,7 +128,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', $this->format_string( $this->get_option( 'subject', $this->get_default_subject() ) ), $this->object );
return apply_filters( 'woocommerce_subscriptions_email_subject_new_renewal_order', parent::get_subject(), $this->object );
}
/**
@ -134,7 +138,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', $this->format_string( $this->get_option( 'heading', $this->get_default_heading() ) ), $this->object );
return apply_filters( 'woocommerce_email_heading_customer_renewal_order', parent::get_heading(), $this->object );
}
/**

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v2.1
* @package WooCommerce_Subscriptions/Classes/Emails
* @author Prospress
* @extends WC_Email
*/
class WCS_Email_Expired_Subscription extends WC_Email {

View File

@ -9,6 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) {
*
* @class WCS_Email_New_Renewal_Order
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
* @extends WC_Email_New_Order
*/
class WCS_Email_New_Renewal_Order extends WC_Email_New_Order {

View File

@ -9,6 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) {
*
* @class WCS_Email_New_Switch_Order
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v1.5
* @extends WC_Email_New_Order
*/
class WCS_Email_New_Switch_Order extends WC_Email_New_Order {

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v2.1
* @package WooCommerce_Subscriptions/Classes/Emails
* @author Prospress
* @extends WC_Email
*/
class WCS_Email_On_Hold_Subscription extends WC_Email {

View File

@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v2.0.0
* @package WooCommerce/Classes/Emails
* @author Prospress
* @extends WC_Email
*/
class WCS_Email_Processing_Renewal_Order extends WC_Email_Customer_Processing_Order {
@ -24,6 +25,9 @@ class WCS_Email_Processing_Renewal_Order extends WC_Email_Customer_Processing_Or
$this->description = __( 'This is an order notification sent to the customer after payment for a subscription renewal order is completed. It contains the renewal order details.', 'woocommerce-subscriptions' );
$this->customer_email = true;
$this->heading = __( 'Thank you for your order', 'woocommerce-subscriptions' );
$this->subject = __( 'Your {site_title} renewal order receipt from {order_date}', 'woocommerce-subscriptions' );
$this->template_html = 'emails/customer-processing-renewal-order.php';
$this->template_plain = 'emails/plain/customer-processing-renewal-order.php';
$this->template_base = WC_Subscriptions_Plugin::instance()->get_plugin_directory( 'templates/' );
@ -45,7 +49,7 @@ class WCS_Email_Processing_Renewal_Order extends WC_Email_Customer_Processing_Or
* @return string
*/
public function get_default_subject() {
return __( 'Your {site_title} renewal order receipt from {order_date}', 'woocommerce-subscriptions' );
return $this->subject;
}
/**
@ -55,7 +59,7 @@ class WCS_Email_Processing_Renewal_Order extends WC_Email_Customer_Processing_Or
* @return string
*/
public function get_default_heading() {
return __( 'Thank you for your order', 'woocommerce-subscriptions' );
return $this->heading;
}
/**

View File

@ -266,14 +266,14 @@ class WC_Subscriptions_Core_Payment_Gateways {
}
$status_html .= '<span class="payment-method-features-info tips" data-tip="';
$status_html .= esc_attr( '<strong><u>' . __( 'Supported features:', 'woocommerce-subscriptions' ) . '</u></strong><br>' . implode( '<br>', str_replace( '_', ' ', $core_features ) ) );
$status_html .= esc_attr( '<strong><u>' . __( 'Supported features:', 'woocommerce-subscriptions' ) . '</u></strong></br>' . implode( '<br />', str_replace( '_', ' ', $core_features ) ) );
if ( ! empty( $subscription_features ) ) {
$status_html .= esc_attr( '<br><strong><u>' . __( 'Subscription features:', 'woocommerce-subscriptions' ) . '</u></strong><br>' . implode( '<br>', str_replace( '_', ' ', $subscription_features ) ) );
$status_html .= esc_attr( '</br><strong><u>' . __( 'Subscription features:', 'woocommerce-subscriptions' ) . '</u></strong></br>' . implode( '<br />', str_replace( '_', ' ', $subscription_features ) ) );
}
if ( ! empty( $change_payment_method_features ) ) {
$status_html .= esc_attr( '<br><strong><u>' . __( 'Change payment features:', 'woocommerce-subscriptions' ) . '</u></strong><br>' . implode( '<br>', str_replace( '_', ' ', $change_payment_method_features ) ) );
$status_html .= esc_attr( '</br><strong><u>' . __( 'Change payment features:', 'woocommerce-subscriptions' ) . '</u></strong></br>' . implode( '<br />', str_replace( '_', ' ', $change_payment_method_features ) ) );
}
$status_html .= '"></span>';

View File

@ -299,7 +299,7 @@ abstract class WCS_SV_API_Base {
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v4.1.0
* @param string $uri current request URI
* @param \WCS_SV_API_Base $instance class instance
* @param \WCS_SV_API_Base $this class instance
*/
return apply_filters( 'wc_' . $this->get_api_id() . '_api_request_uri', $uri, $this );
}
@ -335,7 +335,7 @@ abstract class WCS_SV_API_Base {
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
* @param array $args request arguments
* @param \WCS_SV_API_Base $instance class instance
* @param \WCS_SV_API_Base $this class instance
*/
return apply_filters( 'wc_' . $this->get_api_id() . '_http_request_args', $args, $this );
}

View File

@ -590,7 +590,7 @@ class WCS_PayPal_Reference_Transaction_API_Request {
* Use this to modify the PayPal request parameters prior to validation
*
* @param array $parameters
* @param \WC_PayPal_Reference_Transaction_API_Request $instance instance
* @param \WC_PayPal_Express_API_Request $this instance
*/
$this->parameters = apply_filters( 'wcs_paypal_request_params', $this->parameters, $this );

View File

@ -190,7 +190,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
$transaction_order = wc_get_order( substr( $transaction_details['invoice'], strrpos( $transaction_details['invoice'], '-' ) + 1 ) );
// check if the failed signup has been previously recorded
if ( wcs_get_objects_property( $transaction_order, 'id' ) !== (int) $subscription->get_meta( '_paypal_failed_sign_up_recorded', true ) ) {
if ( wcs_get_objects_property( $transaction_order, 'id' ) !== $subscription->get_meta( '_paypal_failed_sign_up_recorded', true ) ) {
$is_renewal_sign_up_after_failure = true;
}
}

View File

@ -0,0 +1,79 @@
<?php
/**
* Simple Subscription Product Legacy Class
*
* Extends WC_Product_Subscription to provide compatibility methods when running WooCommerce < 3.0.
*
* @class WC_Product_Subscription_Legacy
* @package WooCommerce Subscriptions
* @category Class
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
*
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Product_Subscription_Legacy extends WC_Product_Subscription {
var $subscription_price;
var $subscription_period;
var $subscription_period_interval;
var $subscription_length;
var $subscription_trial_length;
var $subscription_trial_period;
var $subscription_sign_up_fee;
/**
* Create a simple subscription product object.
*
* @access public
* @param mixed $product
*/
public function __construct( $product ) {
parent::__construct( $product );
$this->product_type = 'subscription';
// Load all meta fields
$this->product_custom_fields = get_post_meta( $this->id );
// Convert selected subscription meta fields for easy access
if ( ! empty( $this->product_custom_fields['_subscription_price'][0] ) ) {
$this->subscription_price = $this->product_custom_fields['_subscription_price'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_sign_up_fee'][0] ) ) {
$this->subscription_sign_up_fee = $this->product_custom_fields['_subscription_sign_up_fee'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period'][0] ) ) {
$this->subscription_period = $this->product_custom_fields['_subscription_period'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period_interval'][0] ) ) {
$this->subscription_period_interval = $this->product_custom_fields['_subscription_period_interval'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_length'][0] ) ) {
$this->subscription_length = $this->product_custom_fields['_subscription_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_length'][0] ) ) {
$this->subscription_trial_length = $this->product_custom_fields['_subscription_trial_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_period'][0] ) ) {
$this->subscription_trial_period = $this->product_custom_fields['_subscription_trial_period'][0];
}
$this->subscription_payment_sync_date = ( ! isset( $this->product_custom_fields['_subscription_payment_sync_date'][0] ) ) ? 0 : maybe_unserialize( $this->product_custom_fields['_subscription_payment_sync_date'][0] );
$this->subscription_one_time_shipping = ( ! isset( $this->product_custom_fields['_subscription_one_time_shipping'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_one_time_shipping'][0];
$this->subscription_limit = ( ! isset( $this->product_custom_fields['_subscription_limit'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_limit'][0];
}
}

View File

@ -0,0 +1,101 @@
<?php
/**
* Subscription Product Variation Legacy Class
*
* Extends WC_Product_Subscription_Variation to provide compatibility methods when running WooCommerce < 3.0.
*
* @class WC_Product_Subscription_Variation_Legacy
* @package WooCommerce Subscriptions
* @category Class
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
*
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Product_Subscription_Variation_Legacy extends WC_Product_Subscription_Variation {
/**
* Set default array value for WC 3.0's data property.
* @var array
*/
protected $data = array();
/**
* Create a simple subscription product object.
*
* @access public
* @param mixed $product
*/
public function __construct( $product, $args = array() ) {
parent::__construct( $product, $args = array() );
$this->parent_product_type = $this->product_type;
$this->product_type = 'subscription_variation';
$this->subscription_variation_level_meta_data = array(
'subscription_price' => 0,
'subscription_period' => '',
'subscription_period_interval' => 'day',
'subscription_length' => 0,
'subscription_trial_length' => 0,
'subscription_trial_period' => 'day',
'subscription_sign_up_fee' => 0,
'subscription_payment_sync_date' => 0,
);
$this->variation_level_meta_data = array_merge( $this->variation_level_meta_data, $this->subscription_variation_level_meta_data );
}
/* Copied from WC 2.6 WC_Product_Variation */
/**
* __isset function.
*
* @param mixed $key
* @return bool
*/
public function __isset( $key ) {
if ( in_array( $key, array( 'variation_data', 'variation_has_stock' ) ) ) {
return true;
} elseif ( in_array( $key, array_keys( $this->variation_level_meta_data ) ) ) {
return metadata_exists( 'post', $this->variation_id, '_' . $key );
} elseif ( in_array( $key, array_keys( $this->variation_inherited_meta_data ) ) ) {
return metadata_exists( 'post', $this->variation_id, '_' . $key ) || metadata_exists( 'post', $this->id, '_' . $key );
} else {
return metadata_exists( 'post', $this->id, '_' . $key );
}
}
/**
* Get method returns variation meta data if set, otherwise in most cases the data from the parent.
*
* We need to use the WC_Product_Variation's __get() method, not the one in WC_Product_Subscription_Variation,
* which handles deprecation notices.
*
* @param string $key
* @return mixed
*/
public function __get( $key ) {
return WC_Product_Variation::__get( $key );
}
/**
* Provide a WC 3.0 method for variations.
*
* WC < 3.0 products have a get_parent() method, but this is not equivalent to the get_parent_id() method
* introduced in WC 3.0, because it derives the parent from $this->post->post_parent, but for variations,
* $this->post refers to the parent variable object's post, so $this->post->post_parent will be 0 under
* normal circumstances. Because of that, we can rely on wcs_get_objects_property( $this, 'parent_id' )
* and define this get_parent_id() method for variations even when WC 3.0 is not active.
*
* @param string $key
* @return mixed
*/
public function get_parent_id() {
return $this->id; // When WC < 3.0 is active, the ID property is the parent variable product's ID
}
}

View File

@ -0,0 +1,441 @@
<?php
/**
* Variable Subscription Product Legacy Class
*
* Extends WC_Product_Variable_Subscription to provide compatibility methods when running WooCommerce < 3.0.
*
* @class WC_Product_Variable_Subscription_Legacy
* @package WooCommerce Subscriptions
* @category Class
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
*
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Product_Variable_Subscription_Legacy extends WC_Product_Variable_Subscription {
var $subscription_price;
var $subscription_period;
var $max_variation_period;
var $subscription_period_interval;
var $max_variation_period_interval;
var $product_type;
protected $prices_array;
/**
* Create a simple subscription product object.
*
* @access public
* @param mixed $product
*/
public function __construct( $product ) {
parent::__construct( $product );
$this->parent_product_type = $this->product_type;
$this->product_type = 'variable-subscription';
// Load all meta fields
$this->product_custom_fields = get_post_meta( $this->id );
// Convert selected subscription meta fields for easy access
if ( ! empty( $this->product_custom_fields['_subscription_price'][0] ) ) {
$this->subscription_price = $this->product_custom_fields['_subscription_price'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_sign_up_fee'][0] ) ) {
$this->subscription_sign_up_fee = $this->product_custom_fields['_subscription_sign_up_fee'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period'][0] ) ) {
$this->subscription_period = $this->product_custom_fields['_subscription_period'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_period_interval'][0] ) ) {
$this->subscription_period_interval = $this->product_custom_fields['_subscription_period_interval'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_length'][0] ) ) {
$this->subscription_length = $this->product_custom_fields['_subscription_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_length'][0] ) ) {
$this->subscription_trial_length = $this->product_custom_fields['_subscription_trial_length'][0];
}
if ( ! empty( $this->product_custom_fields['_subscription_trial_period'][0] ) ) {
$this->subscription_trial_period = $this->product_custom_fields['_subscription_trial_period'][0];
}
$this->subscription_payment_sync_date = 0;
$this->subscription_one_time_shipping = ( ! isset( $this->product_custom_fields['_subscription_one_time_shipping'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_one_time_shipping'][0];
$this->subscription_limit = ( ! isset( $this->product_custom_fields['_subscription_limit'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_limit'][0];
}
/**
* Get the min or max variation (active) price.
*
* This is a copy of WooCommerce < 2.4's get_variation_price() method, because 2.4.0 introduced a new
* transient caching system which assumes asort() on prices yields correct results for min/max prices
* (which it does for prices alone, but that's not the full story for subscription prices). Unfortunately,
* the new caching system is also hard to hook into so we'll just use the old system instead as the
* @see self::variable_product_sync() uses the old method also.
*
* @param string $min_or_max - min or max
* @param boolean $display Whether the value is going to be displayed
* @return string
*/
public function get_variation_price( $min_or_max = 'min', $display = false ) {
$variation_id = $this->get_meta( '_' . $min_or_max . '_price_variation_id', true );
if ( $display ) {
if ( $variation = wc_get_product( $variation_id ) ) {
if ( 'incl' == get_option( 'woocommerce_tax_display_shop' ) ) {
$price = wcs_get_price_including_tax( $variation );
} else {
$price = wcs_get_price_excluding_tax( $variation );
}
} else {
$price = '';
}
} else {
$price = $this->get_meta( '_price', true );
}
return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $display );
}
/**
* Get an array of all sale and regular prices from all variations re-ordered after WC has done a standard sort, to reflect subscription terms.
* The first and last element for each price type is the least and most expensive, respectively.
*
* @see WC_Product_Variable::get_variation_prices()
* @param bool $include_taxes Should taxes be included in the prices.
* @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
*/
public function get_variation_prices( $display = false ) {
$price_hash = $this->get_price_hash( $this, $display );
$this->prices_array[ $price_hash ] = parent::get_variation_prices( $display );
$children = array_keys( $this->prices_array[ $price_hash ]['price'] );
sort( $children );
$min_max_data = $this->get_min_and_max_variation_data( $children );
$min_variation_id = $min_max_data['min']['variation_id'];
$max_variation_id = $min_max_data['max']['variation_id'];
// Reorder the variable price arrays to reflect the min and max values so that WooCommerce will find them in the correct order
foreach ( $this->prices_array as $price_hash => $prices ) {
// Loop over sale_price, regular_price & price values to update them on main array
foreach ( $prices as $price_key => $variation_prices ) {
$min_price = $prices[ $price_key ][ $min_variation_id ];
$max_price = $prices[ $price_key ][ $max_variation_id ];
unset( $prices[ $price_key ][ $min_variation_id ] );
unset( $prices[ $price_key ][ $max_variation_id ] );
// append the minimum variation and prepend the maximum variation
$prices[ $price_key ] = array( $min_variation_id => $min_price ) + $prices[ $price_key ];
$prices[ $price_key ] += array( $max_variation_id => $max_price );
$this->prices_array[ $price_hash ][ $price_key ] = $prices[ $price_key ];
}
}
$this->subscription_price = $min_max_data['min']['price'];
$this->subscription_period = $min_max_data['min']['period'];
$this->subscription_period_interval = $min_max_data['min']['interval'];
$this->max_variation_price = $min_max_data['max']['price'];
$this->max_variation_period = $min_max_data['max']['period'];
$this->max_variation_period_interval = $min_max_data['max']['interval'];
$this->min_variation_price = $min_max_data['min']['price'];
$this->min_variation_regular_price = $min_max_data['min']['regular_price'];
return $this->prices_array[ $price_hash ];
}
/**
* Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters.
* DEVELOPERS should filter this hash if offering conditional pricing to keep it unique.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
* @param WC_Product
* @param bool $display Are prices for display? If so, taxes will be calculated.
* @return string
*/
protected function get_price_hash( $display = false ) {
global $wp_filter;
if ( $display ) {
$price_hash = array( get_option( 'woocommerce_tax_display_shop', 'excl' ), WC_Tax::get_rates() );
} else {
$price_hash = array( false );
}
$filter_names = array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' );
foreach ( $filter_names as $filter_name ) {
if ( ! empty( $wp_filter[ $filter_name ] ) ) {
$price_hash[ $filter_name ] = array();
foreach ( $wp_filter[ $filter_name ] as $priority => $callbacks ) {
$price_hash[ $filter_name ][] = array_values( wp_list_pluck( $callbacks, 'function' ) );
}
}
}
$price_hash = md5( json_encode( apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $this, $display ) ) );
return $price_hash;
}
/**
* Sync variable product prices with the children lowest/highest prices.
*
* @param int $product_id The ID of the product.
* @return void
*/
public function variable_product_sync( $product_id = 0 ) {
WC_Product_Variable::variable_product_sync( $product_id );
$child_variation_ids = $this->get_children( true );
if ( $child_variation_ids ) {
$min_max_data = wcs_get_min_max_variation_data( $this, $child_variation_ids );
$this->set_min_and_max_variation_data( $min_max_data, $child_variation_ids );
update_post_meta( $this->id, '_min_price_variation_id', $min_max_data['min']['variation_id'] );
update_post_meta( $this->id, '_max_price_variation_id', $min_max_data['max']['variation_id'] );
update_post_meta( $this->id, '_price', $min_max_data['min']['price'] );
update_post_meta( $this->id, '_min_variation_price', $min_max_data['min']['price'] );
update_post_meta( $this->id, '_max_variation_price', $min_max_data['max']['price'] );
update_post_meta( $this->id, '_min_variation_regular_price', $min_max_data['min']['regular_price'] );
update_post_meta( $this->id, '_max_variation_regular_price', $min_max_data['max']['regular_price'] );
update_post_meta( $this->id, '_min_variation_sale_price', $min_max_data['min']['sale_price'] );
update_post_meta( $this->id, '_max_variation_sale_price', $min_max_data['max']['sale_price'] );
update_post_meta( $this->id, '_min_variation_period', $min_max_data['min']['period'] );
update_post_meta( $this->id, '_max_variation_period', $min_max_data['max']['period'] );
update_post_meta( $this->id, '_min_variation_period_interval', $min_max_data['min']['interval'] );
update_post_meta( $this->id, '_max_variation_period_interval', $min_max_data['max']['interval'] );
update_post_meta( $this->id, '_subscription_price', $min_max_data['min']['price'] );
update_post_meta( $this->id, '_subscription_sign_up_fee', $min_max_data['subscription']['signup-fee'] );
update_post_meta( $this->id, '_subscription_period', $min_max_data['min']['period'] );
update_post_meta( $this->id, '_subscription_period_interval', $min_max_data['min']['interval'] );
update_post_meta( $this->id, '_subscription_trial_period', $min_max_data['subscription']['trial_period'] );
update_post_meta( $this->id, '_subscription_trial_length', $min_max_data['subscription']['trial_length'] );
update_post_meta( $this->id, '_subscription_length', $min_max_data['subscription']['length'] );
$this->subscription_price = $min_max_data['min']['price'];
$this->subscription_period = $min_max_data['min']['period'];
$this->subscription_period_interval = $min_max_data['min']['interval'];
$this->subscription_sign_up_fee = $min_max_data['subscription']['signup-fee'];
$this->subscription_trial_period = $min_max_data['subscription']['trial_period'];
$this->subscription_trial_length = $min_max_data['subscription']['trial_length'];
$this->subscription_length = $min_max_data['subscription']['length'];
if ( function_exists( 'wc_delete_product_transients' ) ) {
wc_delete_product_transients( $this->id );
} else {
WC()->clear_product_transients( $this->id );
}
} else { // No variations yet
$this->subscription_price = '';
$this->subscription_sign_up_fee = '';
$this->subscription_period = 'day';
$this->subscription_period_interval = 1;
$this->subscription_trial_period = 'day';
$this->subscription_trial_length = 1;
$this->subscription_length = 0;
}
}
/**
* Returns the price in html format.
*
* @access public
* @param string $price (default: '')
* @return string
*/
public function get_price_html( $price = '' ) {
if ( ! isset( $this->subscription_period ) || ! isset( $this->subscription_period_interval ) || ! isset( $this->max_variation_period ) || ! isset( $this->max_variation_period_interval ) ) {
$this->variable_product_sync();
}
// Only create the subscription price string when a price has been set
if ( $this->subscription_price !== '' ) {
$price = '';
if ( $this->is_on_sale() && isset( $this->min_variation_price ) && $this->min_variation_regular_price !== $this->get_price() ) {
if ( ! $this->min_variation_price || $this->min_variation_price !== $this->max_variation_price ) {
$price .= wcs_get_price_html_from_text( $this );
}
$variation_id = $this->get_meta( '_min_price_variation_id', true );
$variation = wc_get_product( $variation_id );
$tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
$sale_price_args = array(
'qty' => 1,
'price' => $variation->get_sale_price(),
);
$regular_price_args = array(
'qty' => 1,
'price' => $variation->get_regular_price(),
);
if ( 'incl' == $tax_display_mode ) {
$sale_price = wcs_get_price_including_tax( $variation, $sale_price_args );
$regular_price = wcs_get_price_including_tax( $variation, $regular_price_args );
} else {
$sale_price = wcs_get_price_excluding_tax( $variation, $sale_price_args );
$regular_price = wcs_get_price_excluding_tax( $variation, $regular_price_args );
}
$price .= $this->get_price_html_from_to( $regular_price, $sale_price );
} else {
if ( $this->min_variation_price !== $this->max_variation_price ) {
$price .= wcs_get_price_html_from_text( $this );
}
$price .= wc_price( $this->get_variation_price( 'min', true ) );
}
// Make sure the price contains "From:" when billing schedule differs between variations
if ( false === strpos( $price, wcs_get_price_html_from_text( $this ) ) ) {
if ( $this->subscription_period !== $this->max_variation_period ) {
$price = wcs_get_price_html_from_text( $this ) . $price;
} elseif ( $this->subscription_period_interval !== $this->max_variation_period_interval ) {
$price = wcs_get_price_html_from_text( $this ) . $price;
}
}
$price .= $this->get_price_suffix();
$price = WC_Subscriptions_Product::get_price_string( $this, array( 'price' => $price ) );
}
return apply_filters( 'woocommerce_variable_subscription_price_html', $price, $this );
}
/**
* Provide the WC_Data::get_meta() function when WC < 3.0 is active.
*
* @param string $meta_key
* @param bool $single
* @param string $context
* @return object WC_Product_Subscription or WC_Product_Subscription_Variation
*/
function get_meta( $meta_key = '', $single = true, $context = 'view' ) {
return $this->get_meta( $meta_key, $single );
}
/**
* get_child function.
*
* @access public
* @param mixed $child_id
* @return object WC_Product_Subscription or WC_Product_Subscription_Variation
*/
public function get_child( $child_id ) {
return wc_get_product( $child_id, array(
'product_type' => 'Subscription_Variation',
'parent_id' => $this->id,
'parent' => $this,
) );
}
/**
* Get default attributes.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
* @param string $context
* @return array
*/
public function get_default_attributes( $context = 'view' ) {
return $this->get_variation_default_attributes();
}
/**
* Set the product's min and max variation data.
*
* @param array $min_and_max_data The min and max variation data returned by @see wcs_get_min_max_variation_data(). Optional.
* @param array $variation_ids The visible child variation IDs. Optional. By default this value be generated by @see WC_Product_Variable->get_children( true ).
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.0
*/
public function set_min_and_max_variation_data( $min_and_max_data = array(), $variation_ids = array() ) {
if ( empty( $variation_ids ) ) {
$variation_ids = $this->get_children( true );
}
if ( empty( $min_and_max_data ) ) {
$min_and_max_data = wcs_get_min_max_variation_data( $this, $variation_ids );
}
update_post_meta( $this->id, '_min_max_variation_data', $min_and_max_data, true );
update_post_meta( $this->id, '_min_max_variation_ids_hash', $this->get_variation_ids_hash( $variation_ids ), true );
}
/**
* Get the min and max variation data.
*
* This is a wrapper for @see wcs_get_min_max_variation_data() but to avoid calling
* that resource intensive function multiple times per request, check the value
* stored in meta or cached in memory before calling that function.
*
* @param array $variation_ids An array of variation IDs.
* @return array The variable product's min and max variation data.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.0
*/
public function get_min_and_max_variation_data( $variation_ids ) {
$variation_ids_hash = $this->get_variation_ids_hash( $variation_ids );
// If this variable product has no min and max variation data, set it.
if ( ! metadata_exists( 'post', $this->id, '_min_max_variation_ids_hash' ) ) {
$this->set_min_and_max_variation_data();
}
if ( $variation_ids_hash === $this->get_meta( '_min_max_variation_ids_hash', true ) ) {
$min_and_max_variation_data = $this->get_meta( '_min_max_variation_data', true );
} elseif ( ! empty( $this->min_max_variation_data[ $variation_ids_hash ] ) ) {
$min_and_max_variation_data = $this->min_max_variation_data[ $variation_ids_hash ];
} else {
$min_and_max_variation_data = wcs_get_min_max_variation_data( $this, $variation_ids );
$this->min_max_variation_data[ $variation_ids_hash ] = $min_and_max_variation_data;
}
return $min_and_max_variation_data;
}
}

View File

@ -0,0 +1,776 @@
<?php
/**
* Subscription Legacy Object
*
* Extends WC_Subscription to provide WC 3.0 methods when running WooCommerce < 3.0.
*
* @class WC_Subscription_Legacy
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v2.1
* @package WooCommerce Subscriptions/Classes
* @category Class
* @author Brent Shepherd
*/
class WC_Subscription_Legacy extends WC_Subscription {
protected $schedule;
protected $status_transition = false;
/**
* Whether the object has been read. Pre WC 3.0 subscription objects are always read by default.
* Provides an accessible variable equivalent to WC_Data::$object_read pre WC 3.0.
*
* @protected boolean
*/
protected $object_read = true;
/**
* Initialize the subscription object.
*
* @param int|WC_Subscription $subscription
*/
public function __construct( $subscription ) {
parent::__construct( $subscription );
$this->order_type = 'shop_subscription';
$this->schedule = new stdClass();
}
/**
* Populates a subscription from the loaded post data.
*
* @param mixed $result
*/
public function populate( $result ) {
parent::populate( $result );
if ( $this->post->post_parent > 0 ) {
$this->order = wc_get_order( $this->post->post_parent );
}
}
/**
* Returns the unique ID for this object.
*
* @return int
*/
public function get_id() {
return $this->id;
}
/**
* Get parent order ID.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
* @return int
*/
public function get_parent_id() {
return $this->post->post_parent;
}
/**
* Gets order currency.
*
* @return string
*/
public function get_currency() {
return $this->get_order_currency();
}
/**
* Get customer_note.
*
* @param string $context
* @return string
*/
public function get_customer_note( $context = 'view' ) {
return $this->customer_note;
}
/**
* Get prices_include_tax.
*
* @param string $context
* @return bool
*/
public function get_prices_include_tax( $context = 'view' ) {
return $this->prices_include_tax;
}
/**
* Get the payment method.
*
* @param string $context
* @return string
*/
public function get_payment_method( $context = 'view' ) {
return $this->payment_method;
}
/**
* Get the payment method's title.
*
* @param string $context
* @return string
*/
public function get_payment_method_title( $context = 'view' ) {
return $this->payment_method_title;
}
/** Address Getters **/
/**
* Get billing_first_name.
*
* @param string $context
* @return string
*/
public function get_billing_first_name( $context = 'view' ) {
return $this->billing_first_name;
}
/**
* Get billing_last_name.
*
* @param string $context
* @return string
*/
public function get_billing_last_name( $context = 'view' ) {
return $this->billing_last_name;
}
/**
* Get billing_company.
*
* @param string $context
* @return string
*/
public function get_billing_company( $context = 'view' ) {
return $this->billing_company;
}
/**
* Get billing_address_1.
*
* @param string $context
* @return string
*/
public function get_billing_address_1( $context = 'view' ) {
return $this->billing_address_1;
}
/**
* Get billing_address_2.
*
* @param string $context
* @return string $value
*/
public function get_billing_address_2( $context = 'view' ) {
return $this->billing_address_2;
}
/**
* Get billing_city.
*
* @param string $context
* @return string $value
*/
public function get_billing_city( $context = 'view' ) {
return $this->billing_city;
}
/**
* Get billing_state.
*
* @param string $context
* @return string
*/
public function get_billing_state( $context = 'view' ) {
return $this->billing_state;
}
/**
* Get billing_postcode.
*
* @param string $context
* @return string
*/
public function get_billing_postcode( $context = 'view' ) {
return $this->billing_postcode;
}
/**
* Get billing_country.
*
* @param string $context
* @return string
*/
public function get_billing_country( $context = 'view' ) {
return $this->billing_country;
}
/**
* Get billing_email.
*
* @param string $context
* @return string
*/
public function get_billing_email( $context = 'view' ) {
return $this->billing_email;
}
/**
* Get billing_phone.
*
* @param string $context
* @return string
*/
public function get_billing_phone( $context = 'view' ) {
return $this->billing_phone;
}
/**
* Get shipping_first_name.
*
* @param string $context
* @return string
*/
public function get_shipping_first_name( $context = 'view' ) {
return $this->shipping_first_name;
}
/**
* Get shipping_last_name.
*
* @param string $context
* @return string
*/
public function get_shipping_last_name( $context = 'view' ) {
return $this->shipping_last_name;
}
/**
* Get shipping_company.
*
* @param string $context
* @return string
*/
public function get_shipping_company( $context = 'view' ) {
return $this->shipping_company;
}
/**
* Get shipping_address_1.
*
* @param string $context
* @return string
*/
public function get_shipping_address_1( $context = 'view' ) {
return $this->shipping_address_1;
}
/**
* Get shipping_address_2.
*
* @param string $context
* @return string
*/
public function get_shipping_address_2( $context = 'view' ) {
return $this->shipping_address_2;
}
/**
* Get shipping_city.
*
* @param string $context
* @return string
*/
public function get_shipping_city( $context = 'view' ) {
return $this->shipping_city;
}
/**
* Get shipping_state.
*
* @param string $context
* @return string
*/
public function get_shipping_state( $context = 'view' ) {
return $this->shipping_state;
}
/**
* Get shipping_postcode.
*
* @param string $context
* @return string
*/
public function get_shipping_postcode( $context = 'view' ) {
return $this->shipping_postcode;
}
/**
* Get shipping_country.
*
* @param string $context
* @return string
*/
public function get_shipping_country( $context = 'view' ) {
return $this->shipping_country;
}
/**
* Get order key.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
* @param string $context
* @return string
*/
public function get_order_key( $context = 'view' ) {
return $this->order_key;
}
/**
* Get date_created.
*
* Used by parent::get_date()
*
* @throws WC_Data_Exception
* @return DateTime|NULL object if the date is set or null if there is no date.
*/
public function get_date_created( $context = 'view' ) {
if ( '0000-00-00 00:00:00' != $this->post->post_date_gmt ) {
$datetime = new WC_DateTime( $this->post->post_date_gmt, new DateTimeZone( 'UTC' ) );
$datetime->setTimezone( new DateTimeZone( wc_timezone_string() ) );
} else {
$datetime = new WC_DateTime( $this->post->post_date, new DateTimeZone( wc_timezone_string() ) );
}
// Cache it in $this->schedule for backward compatibility
if ( ! isset( $this->schedule->start ) ) {
$this->schedule->start = wcs_get_datetime_utc_string( $datetime );
}
return $datetime;
}
/**
* Get date_modified.
*
* Used by parent::get_date()
*
* @throws WC_Data_Exception
* @return DateTime|NULL object if the date is set or null if there is no date.
*/
public function get_date_modified( $context = 'view' ) {
if ( '0000-00-00 00:00:00' != $this->post->post_modified_gmt ) {
$datetime = new WC_DateTime( $this->post->post_modified_gmt, new DateTimeZone( 'UTC' ) );
$datetime->setTimezone( new DateTimeZone( wc_timezone_string() ) );
} else {
$datetime = new WC_DateTime( $this->post->post_modified, new DateTimeZone( wc_timezone_string() ) );
}
return $datetime;
}
/**
* Check if a given line item on the subscription had a sign-up fee, and if so, return the value of the sign-up fee.
*
* The single quantity sign-up fee will be returned instead of the total sign-up fee paid. For example, if 3 x a product
* with a 10 BTC sign-up fee was purchased, a total 30 BTC was paid as the sign-up fee but this function will return 10 BTC.
*
* @param array|int Either an order item (in the array format returned by self::get_items()) or the ID of an order item.
* @param string $tax_inclusive_or_exclusive Whether or not to adjust sign up fee if prices inc tax - ensures that the sign up fee paid amount includes the paid tax if inc
* @return bool
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
*/
public function get_items_sign_up_fee( $line_item, $tax_inclusive_or_exclusive = 'exclusive_of_tax' ) {
if ( ! is_array( $line_item ) ) {
$line_item = wcs_get_order_item( $line_item, $this );
}
$parent_order = $this->get_parent();
// If there was no original order, nothing was paid up-front which means no sign-up fee
if ( false == $parent_order ) {
$sign_up_fee = 0;
} else {
$original_order_item = '';
// Find the matching item on the order
foreach ( $parent_order->get_items() as $order_item ) {
if ( wcs_get_canonical_product_id( $line_item ) == wcs_get_canonical_product_id( $order_item ) ) {
$original_order_item = $order_item;
break;
}
}
// No matching order item, so this item wasn't purchased in the original order
if ( empty( $original_order_item ) ) {
$sign_up_fee = 0;
} elseif ( isset( $line_item['item_meta']['_has_trial'] ) ) {
// Sign up is total amount paid for this item on original order when item has a free trial
$sign_up_fee = $original_order_item['line_total'] / $original_order_item['qty'];
} elseif ( isset( $original_order_item['item_meta']['_synced_sign_up_fee'] ) ) {
$sign_up_fee = $original_order_item['item_meta']['_synced_sign_up_fee'] / $original_order_item['qty'];
// The synced sign up fee meta contains the raw product sign up fee, if the subscription totals are inclusive of tax, we need to adjust the synced sign up fee to match tax inclusivity.
if ( $this->get_prices_include_tax() ) {
$line_item_total = $original_order_item['line_total'] + $original_order_item['line_tax'];
$signup_fee_portion = $sign_up_fee / $line_item_total;
$sign_up_fee = $original_order_item['line_total'] * $signup_fee_portion;
}
} else {
// Sign-up fee is any amount on top of recurring amount
$sign_up_fee = max( $original_order_item['line_total'] / $original_order_item['qty'] - $line_item['line_total'] / $line_item['qty'], 0 );
}
// If prices don't inc tax, ensure that the sign up fee amount includes the tax.
if ( 'inclusive_of_tax' === $tax_inclusive_or_exclusive && ! empty( $original_order_item ) && ! empty( $sign_up_fee ) ) {
$sign_up_fee_proportion = $sign_up_fee / ( $original_order_item['line_total'] / $original_order_item['qty'] );
$sign_up_fee_tax = $original_order_item['line_tax'] * $sign_up_fee_proportion;
$sign_up_fee += $sign_up_fee_tax;
$sign_up_fee = wc_format_decimal( $sign_up_fee, wc_get_price_decimals() );
}
}
return apply_filters( 'woocommerce_subscription_items_sign_up_fee', $sign_up_fee, $line_item, $this, $tax_inclusive_or_exclusive );
}
/**
* Helper function to make sure when WC_Subscription calls get_prop() from
* it's new getters that the property is both retrieved from the legacy class
* property and done so from post meta.
*
* For inherited dates props, like date_created, date_modified, date_paid,
* date_completed, we want to use our own get_date() function rather simply
* getting the stored value. Otherwise, we either get the prop set in memory
* or post meta if it's not set yet, because __get() in WC < 3.0 would fallback
* to post meta.
*
* @param string
* @param string
* @return mixed
*/
protected function get_prop( $prop, $context = 'view' ) {
if ( 'switch_data' == $prop ) {
$prop = 'subscription_switch_data';
}
// The requires manual renewal prop uses boolean values but is stored as a string so needs special handling, it also needs to be handled before the checks on $this->$prop to avoid triggering __isset() & __get() magic methods for $this->requires_manual_renewal
if ( 'requires_manual_renewal' === $prop ) {
$value = $this->get_meta( '_' . $prop, true );
if ( 'false' === $value || '' === $value ) {
$value = false;
} else {
$value = true;
}
} elseif ( ! isset( $this->$prop ) || empty( $this->$prop ) ) {
$value = $this->get_meta( '_' . $prop, true );
} else {
$value = $this->$prop;
}
return $value;
}
/**
* Get the stored date for a specific schedule.
*
* @param string $date_type 'date_created', 'trial_end', 'next_payment', 'last_order_date_created' or 'end'
*/
protected function get_date_prop( $date_type ) {
$datetime = parent::get_date_prop( $date_type );
// Cache the string equalivent of it in $this->schedule for backward compatibility
if ( ! isset( $this->schedule->{$date_type} ) ) {
if ( ! is_object( $datetime ) ) {
$this->schedule->{$date_type} = 0;
} else {
$this->schedule->{$date_type} = wcs_get_datetime_utc_string( $datetime );
}
}
return wcs_get_datetime_from( wcs_date_to_time( $datetime ) );
}
/*** Setters *****************************************************/
/**
* Set the unique ID for this object.
*
* @param int
*/
public function set_id( $id ) {
$this->id = absint( $id );
}
/**
* Set parent order ID. We don't use WC_Abstract_Order::set_parent_id() because we want to allow false
* parent IDs, like 0.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
* @param int $value
*/
public function set_parent_id( $value ) {
// Update the parent in the database
wp_update_post( array(
'ID' => $this->id,
'post_parent' => $value,
) );
// And update the parent in memory
$this->post->post_parent = $value;
$this->order = null;
}
/**
* Set subscription status.
*
* @param string $new_status Status to change the order to. No internal wc- prefix is required.
* @return array details of change
*/
public function set_status( $new_status, $note = '', $manual_update = false ) {
$old_status = $this->get_status();
$prefix = substr( $new_status, 0, 3 );
$new_status = 'wc-' === $prefix ? substr( $new_status, 3 ) : $new_status;
wp_update_post(
array(
'ID' => $this->get_id(),
'post_status' => wcs_maybe_prefix_key( $new_status, 'wc-' ),
)
);
$this->post_status = $this->post->post_status = wcs_maybe_prefix_key( $new_status, 'wc-' );
if ( $old_status !== $new_status ) {
$this->status_transition = array(
'from' => ! empty( $this->status_transition['from'] ) ? $this->status_transition['from'] : $old_status,
'to' => $new_status,
'note' => $note,
'manual' => (bool) $manual_update,
);
}
return array(
'from' => $old_status,
'to' => $new_status,
);
}
/**
* Helper function to make sure when WC_Subscription calls set_prop() that property is
* both set in the legacy class property and saved in post meta immediately.
*
* @param string $prop
* @param mixed $value
*/
protected function set_prop( $prop, $value ) {
if ( 'switch_data' == $prop ) {
$prop = 'subscription_switch_data';
}
$this->$prop = $value;
// The requires manual renewal prop uses boolean values but it stored as a string
if ( 'requires_manual_renewal' === $prop ) {
if ( false === $value || '' === $value ) {
$value = 'false';
} else {
$value = 'true';
}
}
update_post_meta( $this->get_id(), '_' . $prop, $value );
}
/**
* Set the stored date for a specific schedule.
*
* @param string $date_type 'trial_end', 'next_payment', 'cancelled', 'payment_retry' or 'end'
* @param int $value UTC timestamp
*/
protected function set_date_prop( $date_type, $value ) {
$datetime = wcs_get_datetime_from( $value );
$date = ! is_null( $datetime ) ? wcs_get_datetime_utc_string( $datetime ) : 0;
$this->set_prop( $this->get_date_prop_key( $date_type ), $date );
$this->schedule->{$date_type} = $date;
}
/**
* Set a certain date type for the last order on the subscription.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
* @param string $date_type
* @param string|integer|object
* @return WC_DateTime|NULL object if the date is set or null if there is no date.
*/
protected function set_last_order_date( $date_type, $date = null ) {
$last_order = $this->get_last_order( 'all' );
if ( $last_order ) {
$datetime = wcs_get_datetime_from( $date );
switch ( $date_type ) {
case 'date_paid':
update_post_meta( $last_order->id, '_paid_date', ! is_null( $date ) ? $datetime->date( 'Y-m-d H:i:s' ) : '' );
// Preemptively set the UTC timestamp for WC 3.0+ also to avoid incorrect values when the site's timezone is changed between now and upgrading to WC 3.0
update_post_meta( $last_order->id, '_date_paid', ! is_null( $date ) ? $datetime->getTimestamp() : '' );
break;
case 'date_completed':
update_post_meta( $last_order->id, '_completed_date', ! is_null( $date ) ? $datetime->date( 'Y-m-d H:i:s' ) : '' );
// Preemptively set the UTC timestamp for WC 3.0+ also to avoid incorrect values when the site's timezone is changed between now and upgrading to WC 3.0
update_post_meta( $last_order->id, '_date_completed', ! is_null( $date ) ? $datetime->getTimestamp() : '' );
break;
case 'date_modified':
wp_update_post( array(
'ID' => $last_order->id,
'post_modified' => $datetime->date( 'Y-m-d H:i:s' ),
'post_modified_gmt' => wcs_get_datetime_utc_string( $datetime ),
) );
break;
case 'date_created':
wp_update_post( array(
'ID' => $last_order->id,
'post_date' => $datetime->date( 'Y-m-d H:i:s' ),
'post_date_gmt' => wcs_get_datetime_utc_string( $datetime ),
) );
break;
}
}
}
/**
* Set date_created.
*
* Used by parent::update_dates()
*
* @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
* @throws WC_Data_Exception
*/
public function set_date_created( $date = null ) {
global $wpdb;
if ( ! is_null( $date ) ) {
$datetime_string = wcs_get_datetime_utc_string( wcs_get_datetime_from( $date ) );
// Don't use wp_update_post() to avoid infinite loops here
$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_date = %s, post_date_gmt = %s WHERE ID = %d", get_date_from_gmt( $datetime_string ), $datetime_string, $this->get_id() ) );
$this->post->post_date = get_date_from_gmt( $datetime_string );
$this->post->post_date_gmt = $datetime_string;
}
}
/**
* Set discount_total.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_discount_total( $value ) {
$this->set_total( $value, 'cart_discount' );
}
/**
* Set discount_tax.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_discount_tax( $value ) {
$this->set_total( $value, 'cart_discount_tax' );
}
/**
* Set shipping_total.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_shipping_total( $value ) {
$this->set_total( $value, 'shipping' );
}
/**
* Set shipping_tax.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_shipping_tax( $value ) {
$this->set_total( $value, 'shipping_tax' );
}
/**
* Set cart tax.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_cart_tax( $value ) {
$this->set_total( $value, 'tax' );
}
/**
* Save data to the database. Nothing to do here as it's all done separately when calling @see this->set_prop().
*
* @return int order ID
*/
public function save() {
$this->status_transition();
return $this->get_id();
}
/**
* Update meta data by key or ID, if provided.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
* @param string $key
* @param string $value
* @param int $meta_id
*/
public function update_meta_data( $key, $value, $meta_id = '' ) {
if ( ! empty( $meta_id ) ) {
update_metadata_by_mid( 'post', $meta_id, $value, $key );
} else {
update_post_meta( $this->get_id(), $key, $value );
}
}
/**
* Save subscription date changes to the database.
* Nothing to do here as all date properties are saved when calling @see $this->set_prop().
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.6
*/
public function save_dates() {
// Nothing to do here.
}
}

View File

@ -0,0 +1,89 @@
<?php
/**
* There is "magic" in PHP, and then there is this.
*
* Story time: once upon a time, in a land not too far away, WooCommerce 3.0 deprecated accessing
* all properties on objects. A conventicle of wizards known as __get(), __set() and __isset() came
* together to make sure that properties on Subscriptions products could still be used, despite not being
* accessible. However, a dark cloud hung over properties which were arrays. None of the conventicle
* new of magic powerful enough to deal with such a problem. Enter Cesar, who summoned the dark arts
* to call upon the ArrayAccess incantation.
*
* In other words, this class is used to access specific items on an array from within the magic methods
* of other objects, like WC_Product_Subscription_Variation::__get() for the property formerly known as
* $subscription_variation_level_meta_data.
*
* @package WooCommerce Subscriptions
* @category Class
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
*
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WCS_Array_Property_Post_Meta_Black_Magic implements ArrayAccess {
/**
* Store the ID this class is being used against so that we use it for post meta calls.
*/
protected $product_id;
/**
* Constructor
*
* @access public
* @param mixed $product
*/
public function __construct( $product_id ) {
$this->product_id = $product_id;
}
/**
* offsetGet
* @param string $key
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet( $key ) {
return get_post_meta( $this->product_id, $this->maybe_prefix_meta_key( $key ), true );
}
/**
* offsetSet
* @param string $key
* @param mixed $value
*/
#[\ReturnTypeWillChange]
public function offsetSet( $key, $value ) {
update_post_meta( $this->product_id, $this->maybe_prefix_meta_key( $key ), $value );
}
/**
* offsetExists
* @param string $key
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists( $key ) {
return metadata_exists( 'post', $this->product_id, $this->maybe_prefix_meta_key( $key ) );
}
/**
* Nothing to do here as we access post meta directly.
*/
#[\ReturnTypeWillChange]
public function offsetUnset( $key ) {
}
/**
* We only work with post meta data that has meta keys prefixed with an underscore, so
* add a prefix if it is not already set.
*/
protected function maybe_prefix_meta_key( $key ) {
if ( '_' != substr( $key, 0, 1 ) ) {
$key = '_' . $key;
}
return $key;
}
}

View File

@ -0,0 +1,41 @@
<?php
/**
* Legacy Subscription Product Handler
*
* Ensures subscription products work with versions of WooCommerce prior to 3.0 by loading
* legacy classes to provide CRUD methods only added with WC 3.0.
*
* @package WooCommerce Subscriptions
* @subpackage WCS_Product_Legacy
* @category Class
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
*/
class WCS_Product_Legacy {
/**
* Set up the class, including it's hooks & filters, when the file is loaded.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
**/
public static function init() {
// Use our legacy product classes when WC 3.0+ is not active
add_filter( 'woocommerce_product_class', __CLASS__ . '::set_product_class', 100, 4 );
}
/**
* Use legacy classes for WC < 3.0
*
* @return string $classname The name of the WC_Product_* class which should be instantiated to create an instance of this product.
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.0
*/
public static function set_product_class( $classname, $product_type, $post_type, $product_id ) {
if ( wcs_is_woocommerce_pre( '3.0' ) && in_array( $classname, array( 'WC_Product_Subscription', 'WC_Product_Variable_Subscription', 'WC_Product_Subscription_Variation' ) ) ) {
$classname .= '_Legacy';
}
return $classname;
}
}

View File

@ -226,10 +226,6 @@ class WC_Subscriptions_Upgrader {
if ( version_compare( self::$stored_plugin_version, '7.8.0', '<' ) ) {
WCS_Plugin_Upgrade_7_8_0::check_gifting_plugin_is_enabled();
}
if ( version_compare( self::$stored_plugin_version, '8.3.0', '<' ) ) {
WCS_Plugin_Upgrade_8_3_0::check_downloads_plugin_is_enabled();
}
}
/**

View File

@ -1,40 +0,0 @@
<?php
/**
* Upgrade script for version 8.1.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WCS_Plugin_Upgrade_8_3_0 {
/**
* Check if the Gifting plugin is enabled and update the settings.
*
* @since 8.1.0
*/
public static function check_downloads_plugin_is_enabled() {
WCS_Upgrade_Logger::add( 'Checking if the Downloads plugin is enabled...' );
$active_plugins = get_option( 'active_plugins', array() );
$is_downloads_plugin_active = false;
foreach ( $active_plugins as $plugin ) {
if ( strpos( $plugin, 'woocommerce-subscription-downloads.php' ) !== false ) {
$is_downloads_plugin_active = true;
break;
}
}
if ( ! $is_downloads_plugin_active ) {
WCS_Upgrade_Logger::add( 'Downloads plugin is not enabled via active_plugins, skipping...' );
return;
}
WCS_Upgrade_Logger::add( 'Downloads plugin is enabled, updating Downloads settings...' );
update_option( WC_Subscriptions_Admin::$option_prefix . '_enable_downloadable_file_linking', 'yes' );
}
}

View File

@ -43,8 +43,6 @@ class WCS_Repair_Suspended_PayPal_Subscriptions extends WCS_Background_Upgrader
* @param int $subscription_id The ID of a shop_subscription/WC_Subscription object.
*/
protected function update_item( $subscription_id ) {
$subscription = null;
try {
$subscription = wcs_get_subscription( $subscription_id );

View File

@ -224,19 +224,19 @@ class WCS_Upgrade_1_2 {
// Upgrading with WC 1.x
if ( is_callable( array( $item_meta, 'add' ) ) ) {
$item_meta->add( '_subscription_period', $order_subscription_periods[ $product_id ] ); // @phpstan-ignore method.notFound (Legacy upgrade code with proper defensive checks)
$item_meta->add( '_subscription_interval', $subscription_interval ); // @phpstan-ignore method.notFound (Legacy upgrade code with proper defensive checks)
$item_meta->add( '_subscription_length', $subscription_length ); // @phpstan-ignore method.notFound (Legacy upgrade code with proper defensive checks)
$item_meta->add( '_subscription_trial_length', $subscription_trial_length ); // @phpstan-ignore method.notFound (Legacy upgrade code with proper defensive checks)
$item_meta->add( '_subscription_period', $order_subscription_periods[ $product_id ] );
$item_meta->add( '_subscription_interval', $subscription_interval );
$item_meta->add( '_subscription_length', $subscription_length );
$item_meta->add( '_subscription_trial_length', $subscription_trial_length );
$item_meta->add( '_subscription_recurring_amount', $order_item['line_subtotal'] ); // @phpstan-ignore method.notFound (Legacy upgrade code with proper defensive checks)
$item_meta->add( '_subscription_sign_up_fee', $subscription_sign_up_fee ); // @phpstan-ignore method.notFound (Legacy upgrade code with proper defensive checks)
$item_meta->add( '_subscription_recurring_amount', $order_item['line_subtotal'] ); // WC_Subscriptions_Product::get_price() would return a price without filters applied
$item_meta->add( '_subscription_sign_up_fee', $subscription_sign_up_fee );
// Set recurring amounts for the item
$item_meta->add( '_recurring_line_total', $order_item['line_total'] ); // @phpstan-ignore method.notFound (Legacy upgrade code with proper defensive checks)
$item_meta->add( '_recurring_line_tax', $order_item['line_tax'] ); // @phpstan-ignore method.notFound (Legacy upgrade code with proper defensive checks)
$item_meta->add( '_recurring_line_subtotal', $order_item['line_subtotal'] ); // @phpstan-ignore method.notFound (Legacy upgrade code with proper defensive checks)
$item_meta->add( '_recurring_line_subtotal_tax', $order_item['line_subtotal_tax'] ); // @phpstan-ignore method.notFound (Legacy upgrade code with proper defensive checks)
$item_meta->add( '_recurring_line_total', $order_item['line_total'] );
$item_meta->add( '_recurring_line_tax', $order_item['line_tax'] );
$item_meta->add( '_recurring_line_subtotal', $order_item['line_subtotal'] );
$item_meta->add( '_recurring_line_subtotal_tax', $order_item['line_subtotal_tax'] );
$order_item['item_meta'] = $item_meta->meta;

View File

@ -884,6 +884,7 @@ class WCS_Upgrade_2_0 {
* @param WC_Subscription $new_subscription A subscription object
* @param WC_Order $switch_order The original order used to purchase the subscription
* @param int $subscription_item_id The order item ID of the item added to the subscription by self::add_product()
* @return null
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
*/
private static function migrate_switch_meta( $new_subscription, $switch_order, $subscription_item_id ) {

View File

@ -79,12 +79,11 @@ function wcs_cart_totals_shipping_html() {
$chosen_recurring_method = empty( $package['rates'] ) ? '' : current( $package['rates'] )->id;
}
$is_package_found = isset( $package['rates'][ $chosen_initial_method ] ) && isset( $initial_packages[ $package_index ] );
$shipping_selection_displayed = false;
$only_one_shipping_option = count( $package['rates'] ) === 1;
$recurring_rates_match_initial_rates = WC_Subscriptions_Cart::package_rates_match_initial_rates( $initial_packages, $package, $recurring_cart_package_key, $recurring_cart );
$recurring_rates_match_initial_rates = isset( $package['rates'][ $chosen_initial_method ] ) && isset( $initial_packages[ $package_index ] ) && $package['rates'] == $initial_packages[ $package_index ]['rates']; // phpcs:ignore WordPress.PHP.StrictComparisons
if ( $only_one_shipping_option || ( $is_package_found && $recurring_rates_match_initial_rates ) ) {
if ( $only_one_shipping_option || ( $recurring_rates_match_initial_rates && apply_filters( 'wcs_cart_totals_shipping_html_price_only', true, $package, $recurring_cart ) ) ) {
$shipping_method = ( 1 === count( $package['rates'] ) ) ? current( $package['rates'] ) : $package['rates'][ $chosen_initial_method ];
// packages match, display shipping amounts only
?>

View File

@ -516,7 +516,7 @@ function wcs_is_checkout_blocks_api_request( $endpoint = '' ) {
* @return bool True if it's a WordPress cron request, false otherwise.
*/
function wcs_doing_cron() {
return function_exists( 'wp_doing_cron' ) ? wp_doing_cron() : defined( 'DOING_CRON' ) && DOING_CRON; // @phpstan-ignore phpstanWP.wpConstant.fetch (Compatibility fallback for older WordPress versions)
return function_exists( 'wp_doing_cron' ) ? wp_doing_cron() : defined( 'DOING_CRON' ) && DOING_CRON;
}
/**
@ -527,7 +527,7 @@ function wcs_doing_cron() {
* @return bool True if it's a WordPress Ajax request, false otherwise.
*/
function wcs_doing_ajax() {
return function_exists( 'wp_doing_ajax' ) ? wp_doing_ajax() : defined( 'DOING_AJAX' ) && DOING_AJAX; // @phpstan-ignore phpstanWP.wpConstant.fetch (Compatibility fallback for older WordPress versions)
return function_exists( 'wp_doing_ajax' ) ? wp_doing_ajax() : defined( 'DOING_AJAX' ) && DOING_AJAX;
}
/**

View File

@ -73,24 +73,13 @@ function wcs_do_subscriptions_exist() {
/**
* Main function for returning subscriptions. Wrapper for the wc_get_order() method.
*
* @since 2.0.0 Introduced.
* @since 8.1.0 Type safety improved.
*
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
* @param mixed $the_subscription Post object or post ID of the order.
*
* @return WC_Subscription|false The subscription object, or false if it cannot be found.
*/
function wcs_get_subscription( $the_subscription ) {
if ( empty( $the_subscription ) ) {
wc_get_logger()->debug(
'Subscription could not be loaded, as an empty value was supplied.',
array(
'backtrace' => true,
'requested_subscription' => $the_subscription,
'requested_subscription_type' => gettype( $the_subscription ),
)
);
return false;
}
@ -98,52 +87,13 @@ function wcs_get_subscription( $the_subscription ) {
$the_subscription = $the_subscription->get_id();
}
/** @var WC_Subscription $subscription */
$subscription = WC()->order_factory->get_order( $the_subscription );
if ( ! wcs_is_subscription( $subscription ) ) {
$subscription = false;
}
/**
* The subscription object being supplied to the caller, or else (bool) false if it could not be loaded.
*
* If a callback turns $subscription into some other type, that value will be rejected and (bool) false
* will ultimately be returned instead.
*
* @since 2.0.0 Introduced.
* @since 8.1.0 Type safety improved.
*
* @param WC_Subscription|false $subscription The subscription object being supplied to the caller, or (bool) false.
*/
$return_val = apply_filters( 'wcs_get_subscription', $subscription );
if ( false === $return_val ) {
wc_get_logger()->debug(
'Subscription could not be loaded.',
array(
'backtrace' => true,
'requested_subscription' => $the_subscription,
'requested_subscription_type' => is_object( $the_subscription ) ? get_class( $the_subscription ) : gettype( $the_subscription ),
)
);
return false;
} elseif ( ! $return_val instanceof WC_Subscription ) {
wc_get_logger()->warning(
'Subscriptions obtained via wcs_get_subscription() must be instances of WC_Subscription.',
array(
'backtrace' => true,
'filtered_type' => is_object( $return_val ) ? get_class( $return_val ) : gettype( $return_val ),
'requested_subscription' => $the_subscription,
'requested_subscription_type' => is_object( $the_subscription ) ? get_class( $the_subscription ) : gettype( $the_subscription ),
)
);
return false;
}
return $return_val;
return apply_filters( 'wcs_get_subscription', $subscription );
}
/**
@ -725,12 +675,7 @@ function wcs_get_subscriptions_for_product( $product_ids, $fields = 'ids', $args
function wcs_get_line_items_with_a_trial( $subscription_id ) {
/** @var WC_Subscription $subscription */
$subscription = ( is_object( $subscription_id ) ) ? $subscription_id : wcs_get_subscription( $subscription_id );
if ( ! $subscription ) {
return array();
}
$trial_items = array();
$trial_items = array();
foreach ( $subscription->get_items() as $line_item_id => $line_item ) {

View File

@ -221,10 +221,7 @@ function wcs_get_calling_function_name() {
*
* Handy when data cached in a transient will be valid even if the transient has expired.
*
* @deprecated 2.3.3
*
* @param string $transient_key The key used to set/get the transient via get_transient()/set_transient()
*
* @return mixed If data exists in a transient, the value of the transient, else boolean false.
*/
function wcs_get_transient_even_if_expired( $transient_key ) {

Some files were not shown because too many files have changed in this diff Show More