This commit is contained in:
Prospress Inc
2019-12-24 15:29:44 +01:00
committed by Remco Tolsma
parent 091a1c9089
commit 92239cc451
38 changed files with 947 additions and 615 deletions

View File

@@ -1,30 +0,0 @@
---
name: "Bug report"
about: Report a bug if something isn't working as expected in Subscriptions
---
### Describe the bug
<!-- A clear and concise description of what the bug is. Please be as descriptive as possible; issues lacking detail, or for any other reason than to report a bug, may be closed without action.-->
### To reproduce
<!-- Describe the steps to reproduce the behavior -->
1.
1.
1.
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem.-->
### Expected behavior
<!-- A clear and concise description of what you expected to happen.-->
### Additional details
<!--Here you can include any additional details you think might be helpful.-->
<!--Ticket numbers/links, plugin versions, system statuses etc.-->
<details><summary>System status</summary>
```
<!--If applicable, paste the system status here. Please ensure you redact or remove any identifying information. -->
```
</details>

View File

@@ -1,17 +0,0 @@
---
name: "New Enhancement"
about: ""
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Proposed approach**
Describe the proposed approach for the enhancement.

3
.github/config.yml vendored
View File

@@ -1,3 +0,0 @@
# helPR bot config. https://probot.github.io/apps/helpr/
helpr:
opened: 'hasPR'

View File

@@ -1,28 +0,0 @@
<!-- Reference the source of this Pull Request. -->
<!-- Remove any which are not applicable. -->
**Issue**: #
**Ticket**:
**Slack thread**:
---
### Description
<!-- Describe the changes made in this Pull Request and the reason for these changes. -->
### Steps to test:
<!-- Describe the steps to replicate the issue and confirm the fix -->
<!-- Try to include as many details as possible. -->
<!-- Please include screenshots. If you work with Prospress, sign-up for CloudApp via https://prospress.cl.ly -->
1.
1.
1.
### Documentation
<!-- Will this change require new documentation or changes to existing documentation? -->
<!-- A good way to answer it is to ask: will more than one customer ever need to know about this? -->
- [ ] This PR needs documentation (has the "status:needs-docs" label).
<!-- For an extra 💯 include further details about which change requires documentation -->
Closes # .

View File

@@ -274,10 +274,10 @@ a.close-subscriptions-search {
} }
.variable_subscription_pricing_2_3 .wc_input_subscription_payment_sync_day { .variable_subscription_pricing_2_3 .wc_input_subscription_payment_sync_day {
max-width: 13%; max-width: 13%;
float: right;
} }
.variable_subscription_pricing_2_3 .wc_input_subscription_payment_sync_month { .variable_subscription_pricing_2_3 .wc_input_subscription_payment_sync_month {
max-width: 86%; max-width: 86%;
float: right;
} }
@media screen and (max-width: 1190px) { @media screen and (max-width: 1190px) {
.variable_subscription_pricing_2_3 p._subscription_price_field, .variable_subscription_pricing_2_3 p._subscription_price_field,
@@ -290,10 +290,10 @@ a.close-subscriptions-search {
} }
.variable_subscription_pricing_2_3 .wc_input_subscription_payment_sync_day { .variable_subscription_pricing_2_3 .wc_input_subscription_payment_sync_day {
max-width: 20%; max-width: 20%;
float: right;
} }
.variable_subscription_pricing_2_3 .wc_input_subscription_payment_sync_month { .variable_subscription_pricing_2_3 .wc_input_subscription_payment_sync_month {
max-width: 78%; max-width: 78%;
float: right;
} }
} }
._subscription_limit_field .description { ._subscription_limit_field .description {

View File

@@ -12,6 +12,10 @@ jQuery(document).ready(function($){
return decodeURIComponent(results[1].replace(/\+/g, ' ')); return decodeURIComponent(results[1].replace(/\+/g, ' '));
} }
}, },
daysInMonth: function( month ) {
// Intentionally choose a non-leap year because we want february to have only 28 days.
return new Date(Date.UTC(2001, month, 0)).getUTCDate();
},
showHideSubscriptionMeta: function(){ showHideSubscriptionMeta: function(){
if ($('select#product-type').val()==WCSubscriptions.productType) { if ($('select#product-type').val()==WCSubscriptions.productType) {
$('.show_if_simple').show(); $('.show_if_simple').show();
@@ -150,7 +154,7 @@ jQuery(document).ready(function($){
if ($('select#product-type').val()=='variable-subscription') { if ($('select#product-type').val()=='variable-subscription') {
var $container = periodField.closest('.woocommerce_variable_attributes').find('.variable_subscription_sync'); var $container = periodField.closest('.woocommerce_variable_attributes').find('.variable_subscription_sync');
} else { } else {
$container = periodField.closest('#general_product_data').find('.subscription_sync') $container = periodField.closest('#general_product_data').find('.subscription_sync');
} }
var $syncWeekMonthContainer = $container.find('.subscription_sync_week_month'), var $syncWeekMonthContainer = $container.find('.subscription_sync_week_month'),
@@ -170,16 +174,17 @@ jQuery(document).ready(function($){
if('day'==billingPeriod) { if('day'==billingPeriod) {
$syncWeekMonthSelect.val(0); $syncWeekMonthSelect.val(0);
$syncAnnualContainer.find('input[type="number"]').val(0); $syncAnnualContainer.find('input[type="number"]').val(0).trigger('change');
} else { } else {
if('year'==billingPeriod) { if('year'==billingPeriod) {
// Make sure the year sync fields are reset // Make sure the year sync fields are reset
$syncAnnualContainer.find('input[type="number"]').val(0); $syncAnnualContainer.find('input[type="number"]').val(0).trigger('change');
// And the week/month field has no option selected // And the week/month field has no option selected
$syncWeekMonthSelect.val(0); $syncWeekMonthSelect.val(0);
} else { } else {
// Make sure the year sync value is 0 // Make sure the year sync value is 0
$syncAnnualContainer.find('input[type="number"]').val(0); $syncAnnualContainer.find('input[type="number"]').val(0).trigger('change');
// And the week/month field has the appropriate options // And the week/month field has the appropriate options
$syncWeekMonthSelect.empty(); $syncWeekMonthSelect.empty();
$.each(WCSubscriptions.syncOptions[billingPeriod], function(key,description) { $.each(WCSubscriptions.syncOptions[billingPeriod], function(key,description) {
@@ -202,6 +207,7 @@ jQuery(document).ready(function($){
if ($varSubField.length > 0) { // Variation if ($varSubField.length > 0) { // Variation
var matches = $varSubField.attr('name').match(/\[(.*?)\]/); var matches = $varSubField.attr('name').match(/\[(.*?)\]/);
$subscriptionPeriodElement = $('[name="variable_subscription_period['+matches[1]+']"]'); $subscriptionPeriodElement = $('[name="variable_subscription_period['+matches[1]+']"]');
if ($('select#product-type').val()=='variable-subscription') { if ($('select#product-type').val()=='variable-subscription') {
$slideSwitch = true; $slideSwitch = true;
} }
@@ -433,6 +439,22 @@ jQuery(document).ready(function($){
$.setTrialPeriods(); $.setTrialPeriods();
}); });
// Handles changes to sync date select/input for yearly subscription products.
$('#woocommerce-product-data').on('change', '[name^="_subscription_payment_sync_date_day"], [name^="variable_subscription_payment_sync_date_day"]', function() {
if ( 0 == $(this).val() ) {
$(this).siblings('[name^="_subscription_payment_sync_date_month"], [name^="variable_subscription_payment_sync_date_month"]').val(0);
$(this).prop('disabled', true);
}
}).on('change', '[name^="_subscription_payment_sync_date_month"], [name^="variable_subscription_payment_sync_date_month"]', function() {
var $syncDayOfMonthInput = $(this).siblings('[name^="_subscription_payment_sync_date_day"], [name^="variable_subscription_payment_sync_date_day"]');
if ( 0 < $(this).val() ) {
$syncDayOfMonthInput.val(1).attr({step: "1", min: "1", max: $.daysInMonth($(this).val())}).prop('disabled', false);
} else {
$syncDayOfMonthInput.val(0).trigger('change');
}
});
$('body').bind('woocommerce-product-type-change',function(){ $('body').bind('woocommerce-product-type-change',function(){
$.showHideSubscriptionMeta(); $.showHideSubscriptionMeta();
$.showHideVariableSubscriptionMeta(); $.showHideVariableSubscriptionMeta();

View File

@@ -1,9 +1,57 @@
*** WooCommerce Subscriptions Changelog *** *** WooCommerce Subscriptions Changelog ***
2019.11.14 - version 2.6.5
* Fix: Account for prorated switch sign up fees in multi-switches. PR#3519
* Fix: Tooltip content displayed on WooCommerce > Subscriptions administration screen.
* Dev: Add additional hooks in My Account Subscription Details table template. PR#3523
2019.11.12 - version 2.6.4
* Tweak: Update the My Account customer has no subscriptions notice to match WC core. PR#3516
* Fix: Add hidden class to subscription_pricing and subscription_sync edit product fields. Fixes a bug when the subscription product types are removed from the edit product type drop-down. PR#3514
* Fix: error on My Account > Payment Methods for non credit card tokens and allow deleting tokens with clear alternative. PR#3482
* Fix: Allow only number of days in specific month for synchronize renewals. PR#3470
* Fix: Save subscription meta box data via set_props(). Fixes fatal error when saving invalid data. PR#3524
* Fix: Use update_option() rather than add_option() to record WC Subscriptions activation. Fixes infinitely running activation hook under some circumstances. PR#3525
* Dev - Opt-in tracking data for Subscriptions PR#3504
Data sent to WooCommerce:
Staging or live site
Live URL
WooCommerce Subscriptions Settings
Dates of the first and last created subscriptions
Number of subscriptions
Number of subscriptions with each status
Gross totals for switch, renewal, resubscribe, and initial totals
Order counts for for switch, renewal, resubscribe, and initial orders
To disable this tracking, opt out of WooCommerce tracking, see https://woocommerce.com/usage-tracking/
2019.10.29 - version 2.6.3
* Fix: use dashicon over fa icon. PR#3497
* Fix: remove button type from close modal link. PR#3497
* Fix: Remove double filtering of the meta label through the woocommerce_attribute_label filter. PR#3476
* Fix: Restore the subscription's end date after reactivation. PR#3399
* Fix: Tooltip content displayed on WooCommerce > Subscriptions administration screen.
* Tweak: Allow modals to be displayed on admin screens. PR#3497
* Tweak: Redirect the customer to checkout after failed early renewal attempt. PR#3494
* Dev: Allow third parties to filter the switch cart item object properties. PR#3484
2019.10.10 - version 2.6.2
* Tweak: Add the switch direction the switch data stored in _subscription_switch_data order meta. PR#3440
* Tweak: Add an order note to manual renewal order to note the order is awaiting customer payment. PR#3456
* Tweak: Add note on the renewal order when a manual payment retry is ran. PR#3477
* Fix: Hide sync meta data on the edit order and subscription screen. PR#3454
* Fix: Fix an issue that led to missing _switched_subscription_item_id line item meta which caused incorrect multi-switch upgrade costs among other issues. PR#3461
* Fix: Store the full set of current subscription counts not just the last element. Fixes issues when exporting subscription report data. PR#3455
* Fix: Add the manually admin requested renewal order notes in the correct order. PR#3462
* Fix: Display the customer facing subscription dates in site time. Fixing display inconsistencies. PR#3469
* Fix: Updated the link in the staging site admin notice. PR#3473
* Fix: Only get return retries from the post store which are retry posts. Fixes an issue where it would return a retry object for other post types. PR#3481
* Fix: Load renewal order fee items to manual renewal carts. PR#3480
* Dev: Fixed WC_Subscription::get_date() returning dates in the site time if the site was using GMT offsets in their site settings. PR#3469
2019.09.04 - version 2.6.1 2019.09.04 - version 2.6.1
* Fix a bug that would lead to switch log entries not including all information. PR#3441 * Fix a bug that would lead to switch log entries not including all information. PR#3441
* Fix fatal errors that would occur on the admin edit order screen on staging sites. PR#3443 * Fix fatal errors that would occur on the admin edit order screen on staging sites. PR#3443
* Performance: Sort subscription related order IDs on the application layer with rsort() instead of MySQL orderby clause. PR#3442 * Performance: Sort using subscription related order IDs on the application layer with rsort() instead of MySQL orderby clause. PR#3442
2019.09.02 - version 2.6.0 2019.09.02 - version 2.6.0
* New: New option to allow customers with automatically renewing subscriptions to renew early via a modal rather than going through the checkout. PR#3293 * New: New option to allow customers with automatically renewing subscriptions to renew early via a modal rather than going through the checkout. PR#3293

View File

@@ -110,9 +110,7 @@ class WC_Subscriptions_Admin {
add_filter( 'posts_where', array( __CLASS__, 'filter_orders' ) ); add_filter( 'posts_where', array( __CLASS__, 'filter_orders' ) );
add_filter( 'posts_where', array( __CLASS__, 'filter_orders_from_list' ) ); add_filter( 'posts_where', array( __CLASS__, 'filter_orders_and_subscriptions_from_list' ) );
add_filter( 'posts_where', array( __CLASS__, 'filter_subscriptions_from_list' ) );
add_filter( 'posts_where', array( __CLASS__, 'filter_paid_subscription_orders_for_user' ) ); add_filter( 'posts_where', array( __CLASS__, 'filter_paid_subscription_orders_for_user' ) );
@@ -288,7 +286,7 @@ class WC_Subscriptions_Admin {
$chosen_period = 'month'; $chosen_period = 'month';
} }
echo '<div class="options_group subscription_pricing show_if_subscription">'; echo '<div class="options_group subscription_pricing show_if_subscription hidden">';
// Subscription Price, Interval and Period // Subscription Price, Interval and Period
?><p class="form-field _subscription_price_fields _subscription_price_field"> ?><p class="form-field _subscription_price_fields _subscription_price_field">
@@ -372,7 +370,7 @@ class WC_Subscriptions_Admin {
global $post; global $post;
echo '</div>'; echo '</div>';
echo '<div class="options_group subscription_one_time_shipping show_if_subscription show_if_variable-subscription">'; echo '<div class="options_group subscription_one_time_shipping show_if_subscription show_if_variable-subscription hidden">';
// Only one Subscription per customer // Only one Subscription per customer
woocommerce_wp_checkbox( array( woocommerce_wp_checkbox( array(
@@ -1455,62 +1453,60 @@ class WC_Subscriptions_Admin {
} }
/** /**
* Filters the Admin orders table results based on a list of IDs returned by a report query. * Filters the Admin orders and subscriptions table results based on a list of IDs returned by a report query.
*
* @since 2.6.2
* *
* @param string $where The query WHERE clause. * @param string $where The query WHERE clause.
* @return string $where * @return string $where
* @since 2.6.0
*/ */
public static function filter_orders_from_list( $where ) { public static function filter_orders_and_subscriptions_from_list( $where ) {
global $typenow, $wpdb; global $typenow, $wpdb;
if ( ! is_admin() || 'shop_order' !== $typenow || ! isset( $_GET['_orders_list_key'], $_GET['_report'] ) ) { if ( ! is_admin() || ! in_array( $typenow, array( 'shop_subscription', 'shop_order' ) ) || ! isset( $_GET['_report'] ) ) {
return $where; return $where;
} }
if ( ! empty( $_GET['_orders_list_key'] ) && ! empty( $_GET['_report'] ) ) { // Map the order or subscription type to their respective keys and type key.
$cache = get_transient( $_GET['_report'] ); $object_type = 'shop_order' === $typenow ? 'order' : 'subscription';
$results = $cache[ $_GET['_orders_list_key'] ]; $cache_report_key = isset( $_GET[ "_{$object_type}s_list_key" ] ) ? $_GET[ "_{$object_type}s_list_key" ] : '';
$order_ids = explode( ',', implode( ',', wp_list_pluck( $results, 'order_ids', true ) ) );
// $format = '%d, %d, %d, %d, %d, [...]' // If the report key or report arg is empty exit early.
$format = implode( ', ', array_fill( 0, count( $order_ids ), '%d' ) ); if ( empty( $cache_report_key ) || empty( $_GET['_report'] ) ) {
$where .= $wpdb->prepare( " AND {$wpdb->posts}.ID IN ($format)", $order_ids );
} else {
// No orders in list. So, give invalid 'where' clause so as to make the query return 0 items.
$where .= " AND {$wpdb->posts}.ID = 0"; $where .= " AND {$wpdb->posts}.ID = 0";
}
return $where;
}
/**
* Filters the Admin subscriptions table results based on a list of IDs returned by a report query.
*
* @param string $where The query WHERE clause.
* @return string
* @since 2.6.0
*/
public static function filter_subscriptions_from_list( $where ) {
global $typenow, $wpdb;
if ( ! is_admin() || 'shop_subscription' !== $typenow || ! isset( $_GET['_subscriptions_list_key'], $_GET['_report'] ) ) {
return $where; return $where;
} }
if ( ! empty( $_GET['_subscriptions_list_key'] ) && ! empty( $_GET['_report'] ) ) { $cache = get_transient( $_GET['_report'] );
$cache = get_transient( $_GET['_report'] );
$results = $cache[ $_GET['_subscriptions_list_key'] ]; // Display an admin notice if we cannot find the report data requested.
$subscription_ids = explode( ',', implode( ',', wp_list_pluck( $results, 'subscription_ids', true ) ) ); if ( ! isset( $cache[ $cache_report_key ] ) ) {
$admin_notice = new WCS_Admin_Notice( 'error' );
$admin_notice->set_simple_content( sprintf(
/* translators: Placeholders are opening and closing link tags. */
__( "We weren't able to locate the set of report results you requested. Please regenerate the link from the %sSubscription Reports screen%s.", 'woocommerce-subscriptions' ),
'<a href="' . esc_url( admin_url( 'admin.php?page=wc-reports&tab=subscriptions&report=subscription_events_by_date' ) ) . '">',
'</a>'
) );
$admin_notice->display();
// $format = '%d, %d, %d, %d, %d, [...]'
$format = implode( ', ', array_fill( 0, count( $subscription_ids ), '%d' ) );
$where .= $wpdb->prepare( " AND {$wpdb->posts}.ID IN ($format)", $subscription_ids );
} else {
// No subscriptions in list. So, give invalid 'where' clause so as to make the query return 0 items.
$where .= " AND {$wpdb->posts}.ID = 0"; $where .= " AND {$wpdb->posts}.ID = 0";
return $where;
} }
$results = $cache[ $cache_report_key ];
// The current subscriptions count report will include the specific result (the subscriptions active on the last day) that should be used to generate the subscription list.
if ( ! empty( $_GET['_data_key'] ) && isset( $results[ (int) $_GET['_data_key'] ] ) ) {
$results = array( $results[ (int) $_GET['_data_key'] ] );
}
$ids = explode( ',', implode( ',', wp_list_pluck( $results, "{$object_type}_ids", true ) ) );
// $format = '%d, %d, %d, %d, %d, [...]'
$format = implode( ', ', array_fill( 0, count( $ids ), '%d' ) );
$where .= $wpdb->prepare( " AND {$wpdb->posts}.ID IN ($format)", $ids );
return $where; return $where;
} }
@@ -2055,4 +2051,34 @@ class WC_Subscriptions_Admin {
public static function recurring_totals_meta_box( $post ) { public static function recurring_totals_meta_box( $post ) {
_deprecated_function( __METHOD__, '2.0' ); _deprecated_function( __METHOD__, '2.0' );
} }
/**
* Filters the Admin orders table results based on a list of IDs returned by a report query.
*
* @deprecated 2.6.2
*
* @param string $where The query WHERE clause.
* @return string $where
* @since 2.6.0
*/
public static function filter_orders_from_list( $where ) {
wcs_deprecated_function( __METHOD__, '2.6.2', 'WC_Subscriptions_Admin::filter_orders_and_subscriptions_from_list( $where )' );
return WC_Subscriptions_Admin::filter_orders_and_subscriptions_from_list( $where );
}
/**
* Filters the Admin subscriptions table results based on a list of IDs returned by a report query.
*
* @deprecated 2.6.2
*
* @param string $where The query WHERE clause.
* @return string
* @since 2.6.0
*/
public static function filter_subscriptions_from_list( $where ) {
wcs_deprecated_function( __METHOD__, '2.6.2', 'WC_Subscriptions_Admin::filter_orders_and_subscriptions_from_list( $where )' );
return WC_Subscriptions_Admin::filter_orders_and_subscriptions_from_list( $where );
}
} }

View File

@@ -179,8 +179,8 @@ class WCS_Admin_Meta_Boxes {
* @since 2.0 * @since 2.0
*/ */
public static function process_renewal_action_request( $subscription ) { public static function process_renewal_action_request( $subscription ) {
do_action( 'woocommerce_scheduled_subscription_payment', $subscription->get_id() );
$subscription->add_order_note( __( 'Process renewal order action requested by admin.', 'woocommerce-subscriptions' ), false, true ); $subscription->add_order_note( __( 'Process renewal order action requested by admin.', 'woocommerce-subscriptions' ), false, true );
do_action( 'woocommerce_scheduled_subscription_payment', $subscription->get_id() );
} }
/** /**
@@ -190,7 +190,7 @@ class WCS_Admin_Meta_Boxes {
* @since 2.0 * @since 2.0
*/ */
public static function create_pending_renewal_action_request( $subscription ) { public static function create_pending_renewal_action_request( $subscription ) {
$subscription->add_order_note( __( 'Create pending renewal order requested by admin action.', 'woocommerce-subscriptions' ), false, true );
$subscription->update_status( 'on-hold' ); $subscription->update_status( 'on-hold' );
$renewal_order = wcs_create_renewal_order( $subscription ); $renewal_order = wcs_create_renewal_order( $subscription );
@@ -203,8 +203,6 @@ class WCS_Admin_Meta_Boxes {
$renewal_order->save(); $renewal_order->save();
} }
} }
$subscription->add_order_note( __( 'Create pending renewal order requested by admin action.', 'woocommerce-subscriptions' ), false, true );
} }
/** /**
@@ -264,6 +262,7 @@ class WCS_Admin_Meta_Boxes {
// init payment gateways // init payment gateways
WC()->payment_gateways(); WC()->payment_gateways();
$order->add_order_note( __( 'Retry renewal payment action requested by admin.', 'woocommerce-subscriptions' ), false, true );
do_action( 'woocommerce_scheduled_subscription_payment_' . wcs_get_objects_property( $order, 'payment_method' ), $order->get_total(), $order ); do_action( 'woocommerce_scheduled_subscription_payment_' . wcs_get_objects_property( $order, 'payment_method' ), $order->get_total(), $order );
} }
} }

View File

@@ -538,7 +538,7 @@ class WCS_Admin_Post_Types {
} }
if ( ! empty( $customer_tip ) ) { if ( ! empty( $customer_tip ) ) {
echo '<div class="tips" data-tip="' . esc_attr( $customer_tip ) . '">'; echo '<div class="tips" data-tip="' . wc_sanitize_tooltip( $customer_tip ) . '">'; // XSS ok.
} }
// This is to stop PHP from complaining // This is to stop PHP from complaining
@@ -1078,7 +1078,7 @@ class WCS_Admin_Post_Types {
$item_html .= wp_kses( $item_name, array( 'a' => array( 'href' => array() ) ) ); $item_html .= wp_kses( $item_name, array( 'a' => array( 'href' => array() ) ) );
if ( $item_meta_html ) { if ( $item_meta_html ) {
$item_html .= wcs_help_tip( $item_meta_html ); $item_html .= wcs_help_tip( $item_meta_html, true );
} }
$item_html .= '</div>'; $item_html .= '</div>';
@@ -1106,7 +1106,7 @@ class WCS_Admin_Post_Types {
echo wp_kses( $item_name, array( 'a' => array( 'href' => array() ) ) ); echo wp_kses( $item_name, array( 'a' => array( 'href' => array() ) ) );
if ( $item_meta_html ) { if ( $item_meta_html ) {
echo wcs_help_tip( $item_meta_html ); echo wcs_help_tip( $item_meta_html, true );
} ?> } ?>
</td> </td>
</tr> </tr>

View File

@@ -315,38 +315,56 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
// Get subscription object. // Get subscription object.
$subscription = wcs_get_subscription( $post_id ); $subscription = wcs_get_subscription( $post_id );
$props = array();
// Ensure there is an order key. // Ensure there is an order key.
if ( ! $subscription->get_order_key() ) { if ( ! $subscription->get_order_key() ) {
wcs_set_objects_property( $subscription, 'order_key', wcs_generate_order_key() ); $props['order_key'] = wcs_generate_order_key();
} }
// Update meta // Update meta
$customer_id = isset( $_POST['customer_user'] ) ? absint( $_POST['customer_user'] ) : 0; $customer_id = isset( $_POST['customer_user'] ) ? absint( $_POST['customer_user'] ) : 0;
if ( $customer_id !== $subscription->get_customer_id() ) { if ( $customer_id !== $subscription->get_customer_id() ) {
wcs_set_objects_property( $subscription, '_customer_user', $customer_id ); $props['customer_id'] = $customer_id;
} }
// Handle the billing fields. // Update billing fields.
foreach ( self::$billing_fields as $key => $field ) { foreach ( self::$billing_fields as $key => $field ) {
$field['id'] = isset( $field['id'] ) ? $field['id'] : "_billing_{$key}"; $field['id'] = isset( $field['id'] ) ? $field['id'] : "_billing_{$key}";
if ( ! isset( $_POST[ $field['id'] ] ) ) { if ( ! isset( $_POST[ $field['id'] ] ) ) {
continue; continue;
} }
wcs_set_objects_property( $subscription, $field['id'], wc_clean( $_POST[ $field['id'] ] ) ); $value = wc_clean( wp_unslash( $_POST[ $field['id'] ] ) );
if ( is_callable( array( $subscription, 'set_billing_' . $key ) ) ) {
$props[ "billing_{$key}" ] = $value;
} else {
$subscription->update_meta_data( $field['id'], $value );
}
} }
// Handle the shipping fields. // Update shipping fields.
foreach ( self::$shipping_fields as $key => $field ) { foreach ( self::$billing_fields as $key => $field ) {
$field['id'] = isset( $field['id'] ) ? $field['id'] : "_shipping_{$key}"; $field['id'] = isset( $field['id'] ) ? $field['id'] : "_shipping_{$key}";
if ( ! isset( $_POST[ $field['id'] ] ) ) { if ( ! isset( $_POST[ $field['id'] ] ) ) {
continue; continue;
} }
wcs_set_objects_property( $subscription, $field['id'], wc_clean( $_POST[ $field['id'] ] ) ); $value = wc_clean( wp_unslash( $_POST[ $field['id'] ] ) );
if ( is_callable( array( $subscription, 'set_billing_' . $key ) ) ) {
$props[ "shipping_{$key}" ] = $value;
} else {
$subscription->update_meta_data( $field['id'], $value );
}
} }
$subscription->set_props( $props );
$subscription->save();
// Save the linked parent order id // Save the linked parent order id
if ( ! empty( $_POST['parent-order-id'] ) ) { if ( ! empty( $_POST['parent-order-id'] ) ) {
// if the parent order to be set is a renewal order // if the parent order to be set is a renewal order

View File

@@ -53,6 +53,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
public function get_data( $args = array() ) { public function get_data( $args = array() ) {
global $wpdb; global $wpdb;
$update_cache = false;
$default_args = array( $default_args = array(
'no_cache' => false, 'no_cache' => false,
'order_status' => apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ), 'order_status' => apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ),
@@ -62,17 +63,17 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$args = wp_parse_args( $args, $default_args ); $args = wp_parse_args( $args, $default_args );
$query_end_date = date( 'Y-m-d', strtotime( '+1 DAY', $this->end_date ) ); $query_end_date = date( 'Y-m-d', strtotime( '+1 DAY', $this->end_date ) );
$offset = get_option( 'gmt_offset' ); $offset = get_option( 'gmt_offset' );
// Convert from Decimal format(eg. 11.5) to a suitable format(eg. +11:30) for CONVERT_TZ() of SQL query. // Convert from Decimal format(eg. 11.5) to a suitable format(eg. +11:30) for CONVERT_TZ() of SQL query.
$site_timezone = sprintf( '%+02d:%02d', (int) $offset, ( $offset - floor( $offset ) ) * 60 ); $site_timezone = sprintf( '%+02d:%02d', (int) $offset, ( $offset - floor( $offset ) ) * 60 );
$this->report_data = new stdClass; $this->report_data = new stdClass;
// While generating report data via get_order_report_data(), hook in to set the query hash so we can cache the results.
add_filter( 'woocommerce_reports_get_order_report_query', array( $this, 'set_query_hash' ) ); add_filter( 'woocommerce_reports_get_order_report_query', array( $this, 'set_query_hash' ) );
$this->generating_report = 'new_subscriptions'; $this->generating_report = 'new_subscriptions';
$this->report_data->new_subscriptions_data = (array) $this->get_order_report_data( $this->report_data->new_subscriptions_data = (array) $this->get_order_report_data(
array( array(
'data' => array( 'data' => array(
@@ -111,8 +112,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
) )
); );
$this->generating_report = 'renewals'; $this->generating_report = 'renewals';
$this->report_data->renewal_data = (array) $this->get_order_report_data( $this->report_data->renewal_data = (array) $this->get_order_report_data(
array( array(
'data' => array( 'data' => array(
@@ -162,8 +162,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
) )
); );
$this->generating_report = 'resubscribes'; $this->generating_report = 'resubscribes';
$this->report_data->resubscribe_data = (array) $this->get_order_report_data( $this->report_data->resubscribe_data = (array) $this->get_order_report_data(
array( array(
'data' => array( 'data' => array(
@@ -213,8 +212,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
) )
); );
$this->generating_report = 'switches'; $this->generating_report = 'switches';
$this->report_data->switch_data = (array) $this->get_order_report_data( $this->report_data->switch_data = (array) $this->get_order_report_data(
array( array(
'data' => array( 'data' => array(
@@ -264,15 +262,24 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
) )
); );
// We've finished generating report data via get_order_report_data() so unhook our query hash flagging function.
unset( $this->generating_report ); unset( $this->generating_report );
remove_filter( 'woocommerce_reports_get_order_report_query', array( $this, 'set_query_hash' ) ); remove_filter( 'woocommerce_reports_get_order_report_query', array( $this, 'set_query_hash' ) );
$cached_results = get_transient( strtolower( get_class( $this ) ) ); $cached_results = get_transient( strtolower( get_class( $this ) ) );
// Check if we need to update the cache with the query results from the figures generated by get_order_report_data().
foreach ( array( 'new_subscriptions' => 'new_subscriptions', 'renewals' => 'renewal', 'resubscribes' => 'resubscribe', 'switches' => 'switch' ) as $report => $property_key ) {
$query_hash = $this->report_data->{"{$report}_query_hash"};
if ( ! isset( $cached_results[ $query_hash ] ) ) {
$cached_results[ $query_hash ] = $this->report_data->{"{$property_key}_data"};
$update_cache = true;
}
}
/* /*
* New subscription orders * New subscription orders
*/ */
$query = $wpdb->prepare( $query = $wpdb->prepare(
"SELECT SUM(subscriptions.count) as count, "SELECT SUM(subscriptions.count) as count,
order_posts.post_date as post_date, order_posts.post_date as post_date,
@@ -310,7 +317,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_sign_up_data', (array) $wpdb->get_results( $query ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_sign_up_data', (array) $wpdb->get_results( $query ), $args );
set_transient( strtolower( get_class( $this ) ), $cached_results, WEEK_IN_SECONDS ); $update_cache = true;
} }
$this->report_data->signup_data = $cached_results[ $query_hash ]; $this->report_data->signup_data = $cached_results[ $query_hash ];
@@ -375,12 +382,10 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_subscriber_count_data', (array) $wpdb->get_results( $query ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_subscriber_count_data', (array) $wpdb->get_results( $query ), $args );
set_transient( strtolower( get_class( $this ) ), $cached_results, WEEK_IN_SECONDS ); $update_cache = true;
} }
$this->report_data->subscriber_counts = $cached_results[ $query_hash ]; $this->report_data->subscriber_counts = $cached_results[ $query_hash ];
$cached_results[ $query_hash ] = array_slice( $this->report_data->subscriber_counts, -1 );
$this->report_data->current_subscriptions_query_hash = $query_hash; $this->report_data->current_subscriptions_query_hash = $query_hash;
/* /*
@@ -406,7 +411,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_cancel_count_data', (array) $wpdb->get_results( $query ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_cancel_count_data', (array) $wpdb->get_results( $query ), $args );
set_transient( strtolower( get_class( $this ) ), $cached_results, WEEK_IN_SECONDS ); $update_cache = true;
} }
$this->report_data->cancel_counts = $cached_results[ $query_hash ]; $this->report_data->cancel_counts = $cached_results[ $query_hash ];
@@ -436,7 +441,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) { if ( $args['no_cache'] || false === $cached_results || ! isset( $cached_results[ $query_hash ] ) ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_ended_count_data', (array) $wpdb->get_results( $query ), $args ); $cached_results[ $query_hash ] = apply_filters( 'wcs_reports_subscription_events_ended_count_data', (array) $wpdb->get_results( $query ), $args );
set_transient( strtolower( get_class( $this ) ), $cached_results, WEEK_IN_SECONDS ); $update_cache = true;
} }
$this->report_data->ended_counts = $cached_results[ $query_hash ]; $this->report_data->ended_counts = $cached_results[ $query_hash ];
@@ -458,6 +463,14 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$this->report_data->total_subscriptions_at_period_end = $this->report_data->subscriber_counts ? absint( end( $this->report_data->subscriber_counts )->count ) : 0; $this->report_data->total_subscriptions_at_period_end = $this->report_data->subscriber_counts ? absint( end( $this->report_data->subscriber_counts )->count ) : 0;
$this->report_data->total_subscriptions_at_period_start = isset( $this->report_data->subscriber_counts[0]->count ) ? absint( $this->report_data->subscriber_counts[0]->count ) : 0; $this->report_data->total_subscriptions_at_period_start = isset( $this->report_data->subscriber_counts[0]->count ) ? absint( $this->report_data->subscriber_counts[0]->count ) : 0;
if ( $update_cache ) {
set_transient( strtolower( get_class( $this ) ), $cached_results, WEEK_IN_SECONDS );
// Remove this class from the list of classes WC updates on shutdown. Introduced in WC 3.7
if ( ! WC_Subscriptions::is_woocommerce_pre( '3.7' ) ) {
unset( WC_Admin_Report::$transients_to_update[ strtolower( get_class( $this ) ) ] );
}
}
} }
/** /**
@@ -552,9 +565,12 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
'highlight_series' => 6, 'highlight_series' => 6,
); );
// For the subscriptions count we only need to display the subscriptions included on the last day of the report period so pass the last cache key. The array keys are integers so using max() returns the last array key.
$data_key = max( array_keys( $this->report_data->subscriber_counts ) );
$legend[] = array( $legend[] = array(
'title' => sprintf( __( '%2$s %1$s current subscriptions', 'woocommerce-subscriptions' ), '<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->total_subscriptions_at_period_end . '</strong> </a>', 'title' => sprintf( __( '%2$s %1$s current subscriptions', 'woocommerce-subscriptions' ), '<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->total_subscriptions_at_period_end . '</strong> </a>',
'<a href="' . esc_url( add_query_arg( array( 'post_type' => 'shop_subscription', '_subscriptions_list_key' => $this->report_data->current_subscriptions_query_hash, '_report' => strtolower( get_class( $this ) ) ), admin_url( 'edit.php' ) ) ). '">' ), '<a href="' . esc_url( add_query_arg( array( 'post_type' => 'shop_subscription', '_subscriptions_list_key' => $this->report_data->current_subscriptions_query_hash, '_report' => strtolower( get_class( $this ) ), '_data_key' => $data_key ), admin_url( 'edit.php' ) ) ). '">' ),
'placeholder' => __( 'The number of subscriptions during this period with an end date in the future and a status other than pending.', 'woocommerce-subscriptions' ), 'placeholder' => __( 'The number of subscriptions during this period with an end date in the future and a status other than pending.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['subscriber_count'], 'color' => $this->chart_colours['subscriber_count'],
'highlight_series' => 5, 'highlight_series' => 5,

View File

@@ -434,6 +434,8 @@ class WC_Subscription extends WC_Order {
break; break;
case 'pending-cancel' : case 'pending-cancel' :
// Store the subscription's end date before overriding it. Used for restoring the dates if the customer reactivates the subscription.
$this->update_meta_data( 'end_date_pre_cancellation', $this->get_date( 'end' ) );
$end_date = $this->calculate_date( 'end_of_prepaid_term' ); $end_date = $this->calculate_date( 'end_of_prepaid_term' );
@@ -456,7 +458,7 @@ class WC_Subscription extends WC_Order {
if ( 'pending-cancel' === $old_status ) { if ( 'pending-cancel' === $old_status ) {
$this->update_dates( array( $this->update_dates( array(
'cancelled' => 0, 'cancelled' => 0,
'end' => 0, 'end' => $this->meta_exists( 'end_date_pre_cancellation' ) ? $this->get_meta( 'end_date_pre_cancellation' ) : 0,
'next_payment' => $this->get_date( 'end' ), 'next_payment' => $this->get_date( 'end' ),
) ); ) );
} else { } else {
@@ -1016,7 +1018,7 @@ class WC_Subscription extends WC_Order {
$date->setTimezone( new DateTimeZone( 'UTC' ) ); $date->setTimezone( new DateTimeZone( 'UTC' ) );
} }
$date = $date->format( 'Y-m-d H:i:s' ); $date = $date->date( 'Y-m-d H:i:s' );
} }
return apply_filters( 'woocommerce_subscription_get_' . $date_type . '_date', $date, $this, $timezone ); return apply_filters( 'woocommerce_subscription_get_' . $date_type . '_date', $date, $this, $timezone );
@@ -1228,8 +1230,6 @@ class WC_Subscription extends WC_Order {
* @param string $timezone The timezone of the $datetime param. Default 'gmt'. * @param string $timezone The timezone of the $datetime param. Default 'gmt'.
*/ */
public function update_dates( $dates, $timezone = 'gmt' ) { public function update_dates( $dates, $timezone = 'gmt' ) {
global $wpdb;
$dates = $this->validate_date_updates( $dates, $timezone ); $dates = $this->validate_date_updates( $dates, $timezone );
// If an exception hasn't been thrown by this point, we can safely update the dates // If an exception hasn't been thrown by this point, we can safely update the dates
@@ -2338,7 +2338,7 @@ class WC_Subscription extends WC_Order {
continue; continue;
} }
// We don't want to validate dates for relates orders when instantiating the subscription // We don't want to validate dates for related orders when instantiating the subscription
if ( false === $this->object_read && ( 0 === strpos( $date_type, 'last_order_date_' ) || in_array( $date_type, array( 'date_paid', 'date_completed' ) ) ) ) { if ( false === $this->object_read && ( 0 === strpos( $date_type, 'last_order_date_' ) || in_array( $date_type, array( 'date_paid', 'date_completed' ) ) ) ) {
continue; continue;
} }

View File

@@ -134,6 +134,7 @@ class WC_Subscriptions_Manager {
if ( $subscription->is_manual() ) { if ( $subscription->is_manual() ) {
do_action( 'woocommerce_generated_manual_renewal_order', wcs_get_objects_property( $renewal_order, 'id' ), $subscription ); do_action( 'woocommerce_generated_manual_renewal_order', wcs_get_objects_property( $renewal_order, 'id' ), $subscription );
$renewal_order->add_order_note( __( 'Manual renewal order awaiting customer payment.', 'woocommerce-subscriptions' ) );
} else { } else {
$renewal_order->set_payment_method( wc_get_payment_gateway_by_order( $subscription ) ); // We need to pass the payment gateway instance to be compatible with WC < 3.0, only WC 3.0+ supports passing the string name $renewal_order->set_payment_method( wc_get_payment_gateway_by_order( $subscription ) ); // We need to pass the payment gateway instance to be compatible with WC < 3.0, only WC 3.0+ supports passing the string name

View File

@@ -668,13 +668,16 @@ class WC_Subscriptions_Product {
public static function set_subscription_variation_class( $classname, $product_type, $post_type, $product_id ) { public static function set_subscription_variation_class( $classname, $product_type, $post_type, $product_id ) {
if ( 'product_variation' === $post_type && 'variation' === $product_type ) { if ( 'product_variation' === $post_type && 'variation' === $product_type ) {
$post = get_post( $product_id );
$terms = get_the_terms( get_post( $product_id )->post_parent, 'product_type' ); if ( $post ) {
$terms = get_the_terms( $post->post_parent, 'product_type' );
$parent_product_type = ! empty( $terms ) && isset( current( $terms )->slug ) ? current( $terms )->slug : ''; $parent_product_type = ! empty( $terms ) && isset( current( $terms )->slug ) ? current( $terms )->slug : '';
if ( 'variable-subscription' === $parent_product_type ) { if ( 'variable-subscription' === $parent_product_type ) {
$classname = 'WC_Product_Subscription_Variation'; $classname = 'WC_Product_Subscription_Variation';
}
} }
} }

View File

@@ -820,8 +820,9 @@ class WC_Subscriptions_Switcher {
*/ */
public static function add_line_item_meta( $order_item, $cart_item_key, $cart_item, $order ) { public static function add_line_item_meta( $order_item, $cart_item_key, $cart_item, $order ) {
if ( isset( $cart_item['subscription_switch'] ) ) { if ( isset( $cart_item['subscription_switch'] ) ) {
if ( $switches = self::cart_contains_switches( 'any' ) && isset( $switches[ $cart_item_key ] ) ) { $switches = self::cart_contains_switches( 'any' );
if ( isset( $switches[ $cart_item_key ] ) ) {
$switch_details = $switches[ $cart_item_key ]; $switch_details = $switches[ $cart_item_key ];
if ( wcs_is_subscription( $order ) ) { if ( wcs_is_subscription( $order ) ) {
@@ -829,8 +830,8 @@ class WC_Subscriptions_Switcher {
$order_item->add_meta_data( '_switched_subscription_item_id', $switch_details['item_id'] ); $order_item->add_meta_data( '_switched_subscription_item_id', $switch_details['item_id'] );
} }
} else { } else {
$sign_up_fee_prorated = WC()->cart->cart_contents[ $cart_item_key ]['data']->get_meta( 'subscription_sign_up_fee_prorated', true ); $sign_up_fee_prorated = WC()->cart->cart_contents[ $cart_item_key ]['data']->get_meta( '_subscription_sign_up_fee_prorated', true );
$price_prorated = WC()->cart->cart_contents[ $cart_item_key ]['data']->get_meta( 'subscription_price_prorated', true ); $price_prorated = WC()->cart->cart_contents[ $cart_item_key ]['data']->get_meta( '_subscription_price_prorated', true );
$order_item->add_meta_data( '_switched_subscription_sign_up_fee_prorated', empty( $sign_up_fee_prorated ) ? 0 : $sign_up_fee_prorated ); $order_item->add_meta_data( '_switched_subscription_sign_up_fee_prorated', empty( $sign_up_fee_prorated ) ? 0 : $sign_up_fee_prorated );
$order_item->add_meta_data( '_switched_subscription_price_prorated', empty( $price_prorated ) ? 0 : $price_prorated ); $order_item->add_meta_data( '_switched_subscription_price_prorated', empty( $price_prorated ) ? 0 : $price_prorated );
} }
@@ -912,10 +913,11 @@ class WC_Subscriptions_Switcher {
$switched_item_data = array(); $switched_item_data = array();
if ( ! empty( $cart_item['subscription_switch']['item_id'] ) ) { if ( ! empty( $cart_item['subscription_switch']['item_id'] ) ) {
$switched_item_data['remove_line_item'] = $cart_item['subscription_switch']['item_id'];
$existing_item = wcs_get_order_item( $cart_item['subscription_switch']['item_id'], $subscription ); $existing_item = wcs_get_order_item( $cart_item['subscription_switch']['item_id'], $subscription );
$switch_item = new WCS_Switch_Cart_Item( $cart_item, $subscription, $existing_item ); $switch_item = new WCS_Switch_Cart_Item( $cart_item, $subscription, $existing_item );
$is_switch_with_matching_trials = $switch_item->is_switch_during_trial() && $switch_item->trial_periods_match(); $is_switch_with_matching_trials = $switch_item->is_switch_during_trial() && $switch_item->trial_periods_match();
$switched_item_data['remove_line_item'] = $cart_item['subscription_switch']['item_id'];
$switched_item_data['switch_direction'] = $switch_item->get_switch_type();
} }
// If the item is on the same schedule, we can just add it to the new subscription and remove the old item. // If the item is on the same schedule, we can just add it to the new subscription and remove the old item.
@@ -2253,16 +2255,21 @@ class WC_Subscriptions_Switcher {
} }
// Remove any signup fees if necessary. // Remove any signup fees if necessary.
if ( $order_is_parent && 'include_sign_up_fees' !== $include_sign_up_fees ) { if ( 'include_sign_up_fees' !== $include_sign_up_fees ) {
if ( $order_item->meta_exists( '_synced_sign_up_fee' ) ) { if ( $order_is_parent ) {
$item_total -= $order_item->get_meta( '_synced_sign_up_fee' ); if ( $order_item->meta_exists( '_synced_sign_up_fee' ) ) {
} elseif ( $subscription_item->meta_exists( '_has_trial' ) ) { $item_total -= $order_item->get_meta( '_synced_sign_up_fee' ) * $order_item->get_quantity();
// Where there's a free trial, the sign up fee is the entire item total so the non-sign-up fee portion is 0. } elseif ( $subscription_item->meta_exists( '_has_trial' ) ) {
$item_total = 0; // Where there's a free trial, the sign up fee is the entire item total so the non-sign-up fee portion is 0.
} else { $item_total = 0;
// For non-free trial subscriptions, the sign up fee portion is the order total minus the recurring total (subscription item total). } else {
// Use the subscription item's subtotal (without discounts) to avoid signup fee coupon discrepancies // For non-free trial subscriptions, the sign up fee portion is the order total minus the recurring total (subscription item total).
$item_total -= max( $order_item->get_total() - $subscription_item->get_subtotal(), 0 ); // Use the subscription item's subtotal (without discounts) to avoid signup fee coupon discrepancies
$item_total -= max( $order_item->get_total() - $subscription_item->get_subtotal(), 0 );
}
// Remove the prorated sign up fees.
} elseif ( $order_item->meta_exists( '_switched_subscription_sign_up_fee_prorated' ) ) {
$item_total -= $order_item->get_meta( '_switched_subscription_sign_up_fee_prorated' ) * $order_item->get_quantity();
} }
} }

View File

@@ -122,6 +122,9 @@ class WC_Subscriptions_Synchroniser {
// Ensure options are the proper type. // Ensure options are the proper type.
add_filter( 'option_' . self::$setting_id_days_no_fee, 'intval' ); add_filter( 'option_' . self::$setting_id_days_no_fee, 'intval' );
// Don't display migrated order item meta on the Edit Order screen
add_filter( 'woocommerce_hidden_order_itemmeta', array( __CLASS__, 'hide_order_itemmeta' ) );
} }
/** /**
@@ -273,13 +276,13 @@ class WC_Subscriptions_Synchroniser {
// An annual sync date is already set in the form: array( 'day' => 'nn', 'month' => 'nn' ), create a MySQL string from those values (year and time are irrelvent as they are ignored) // An annual sync date is already set in the form: array( 'day' => 'nn', 'month' => 'nn' ), create a MySQL string from those values (year and time are irrelvent as they are ignored)
if ( is_array( $payment_day ) ) { if ( is_array( $payment_day ) ) {
$payment_month = $payment_day['month']; $payment_month = ( 0 === (int) $payment_day['day'] ) ? 0 : $payment_day['month'];
$payment_day = $payment_day['day']; $payment_day = $payment_day['day'];
} else { } else {
$payment_month = gmdate( 'm' ); $payment_month = 0;
} }
echo '<div class="options_group subscription_pricing subscription_sync show_if_subscription">'; echo '<div class="options_group subscription_pricing subscription_sync show_if_subscription hidden">';
echo '<div class="subscription_sync_week_month" style="' . esc_attr( $display_week_month_select ) . '">'; echo '<div class="subscription_sync_week_month" style="' . esc_attr( $display_week_month_select ) . '">';
woocommerce_wp_select( array( woocommerce_wp_select( array(
@@ -300,16 +303,16 @@ class WC_Subscriptions_Synchroniser {
?><p class="form-field _subscription_payment_sync_date_day_field"> ?><p class="form-field _subscription_payment_sync_date_day_field">
<label for="_subscription_payment_sync_date_day"><?php echo esc_html( self::$sync_field_label ); ?></label> <label for="_subscription_payment_sync_date_day"><?php echo esc_html( self::$sync_field_label ); ?></label>
<span class="wrap"> <span class="wrap">
<input type="number" id="<?php echo esc_attr( self::$post_meta_key_day ); ?>" name="<?php echo esc_attr( self::$post_meta_key_day ); ?>" class="wc_input_subscription_payment_sync" value="<?php echo esc_attr( $payment_day ); ?>" placeholder="<?php echo esc_attr_x( 'Day', 'input field placeholder for day field for annual subscriptions', 'woocommerce-subscriptions' ); ?>" />
<label for="<?php echo esc_attr( self::$post_meta_key_month ); ?>" class="wcs_hidden_label"><?php esc_html_e( 'Month for Synchronisation', 'woocommerce-subscriptions' ); ?></label> <label for="<?php echo esc_attr( self::$post_meta_key_month ); ?>" class="wcs_hidden_label"><?php esc_html_e( 'Month for Synchronisation', 'woocommerce-subscriptions' ); ?></label>
<select id="<?php echo esc_attr( self::$post_meta_key_month ); ?>" name="<?php echo esc_attr( self::$post_meta_key_month ); ?>" class="wc_input_subscription_payment_sync last" > <select id="<?php echo esc_attr( self::$post_meta_key_month ); ?>" name="<?php echo esc_attr( self::$post_meta_key_month ); ?>" class="wc_input_subscription_payment_sync last" >
<?php foreach ( $wp_locale->month as $value => $label ) { ?> <?php foreach ( self::get_year_sync_options() as $value => $label ) { ?>
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $payment_month, true ) ?>><?php echo esc_html( $label ); ?></option> <option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, $payment_month, true ) ?>><?php echo esc_html( $label ); ?></option>
<?php } ?> <?php } ?>
</select> </select>
</select> <?php $daysInMonth = $payment_month ? cal_days_in_month( CAL_GREGORIAN, (int) $payment_month, 2001 ) : 0; ?>
<input type="number" id="<?php echo esc_attr( self::$post_meta_key_day ); ?>" name="<?php echo esc_attr( self::$post_meta_key_day ); ?>" class="wc_input_subscription_payment_sync" value="<?php echo esc_attr( $payment_day ); ?>" placeholder="<?php echo esc_attr_x( 'Day', 'input field placeholder for day field for annual subscriptions', 'woocommerce-subscriptions' ); ?>" step="1" min="<?php echo esc_attr( min( 1, $daysInMonth ) ); ?>" max="<?php echo esc_attr( $daysInMonth ); ?>" <?php disabled( 0, $payment_month, true ); ?> />
</span> </span>
<?php echo wcs_help_tip( self::$sync_description_year ); ?> <?php echo wcs_help_tip( self::$sync_description_year ); ?>
</p><?php </p><?php
@@ -343,10 +346,10 @@ class WC_Subscriptions_Synchroniser {
// An annual sync date is already set in the form: array( 'day' => 'nn', 'month' => 'nn' ), create a MySQL string from those values (year and time are irrelvent as they are ignored) // An annual sync date is already set in the form: array( 'day' => 'nn', 'month' => 'nn' ), create a MySQL string from those values (year and time are irrelvent as they are ignored)
if ( is_array( $payment_day ) ) { if ( is_array( $payment_day ) ) {
$payment_month = $payment_day['month']; $payment_month = ( 0 === (int) $payment_day['day'] ) ? 0 : $payment_day['month'];
$payment_day = $payment_day['day']; $payment_day = $payment_day['day'];
} else { } else {
$payment_month = gmdate( 'm' ); $payment_month = 0;
} }
include( plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'templates/admin/html-variation-synchronisation.php' ); include( plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'templates/admin/html-variation-synchronisation.php' );
@@ -446,8 +449,8 @@ class WC_Subscriptions_Synchroniser {
$script_parameters['syncOptions'] = array( $script_parameters['syncOptions'] = array(
'week' => $billing_period_strings['week'], 'week' => $billing_period_strings['week'],
'month' => $billing_period_strings['month'], 'month' => $billing_period_strings['month'],
'year' => self::get_year_sync_options(),
); );
} }
return $script_parameters; return $script_parameters;
@@ -711,6 +714,20 @@ class WC_Subscriptions_Synchroniser {
return apply_filters( 'woocommerce_subscriptions_synced_first_payment_date', $first_payment, $product, $type, $from_date, $from_date_param ); return apply_filters( 'woocommerce_subscriptions_synced_first_payment_date', $first_payment, $product, $type, $from_date, $from_date_param );
} }
/**
* Return an i18n'ified associative array of sync options for 'year' as billing period
*
* @since 3.0.0
*/
public static function get_year_sync_options() {
global $wp_locale;
$year_sync_options[0] = __( 'Do not synchronise', 'woocommerce-subscriptions' );
$year_sync_options += $wp_locale->month;
return $year_sync_options;
}
/** /**
* Return an i18n'ified associative array of all possible subscription periods. * Return an i18n'ified associative array of all possible subscription periods.
* *
@@ -721,7 +738,7 @@ class WC_Subscriptions_Synchroniser {
if ( empty( self::$billing_period_ranges ) ) { if ( empty( self::$billing_period_ranges ) ) {
foreach ( array( 'week', 'month', 'year' ) as $key ) { foreach ( array( 'week', 'month' ) as $key ) {
self::$billing_period_ranges[ $key ][0] = __( 'Do not synchronise', 'woocommerce-subscriptions' ); self::$billing_period_ranges[ $key ][0] = __( 'Do not synchronise', 'woocommerce-subscriptions' );
} }
@@ -1228,6 +1245,22 @@ class WC_Subscriptions_Synchroniser {
} }
} }
/**
* Hides synced subscription meta on the edit order and subscription screen on non-debug sites.
*
* @since 2.6.2
* @param array $hidden_meta_keys the list of meta keys hidden on the edit order and subscription screen.
* @return array $hidden_meta_keys
*/
public static function hide_order_itemmeta( $hidden_meta_keys ) {
if ( apply_filters( 'woocommerce_subscriptions_hide_synchronization_itemmeta', ! defined( 'WCS_DEBUG' ) || true !== WCS_DEBUG ) ) {
$hidden_meta_keys[] = '_synced_sign_up_fee';
}
return $hidden_meta_keys;
}
/* Deprecated Functions */ /* Deprecated Functions */
/** /**

View File

@@ -0,0 +1,198 @@
<?php
/**
* Tracker for Subscriptions usage.
*
* @class WC_Subscriptions_Tracker
* @version 2.6.4
* @package WooCommerce Subscriptions/Classes
* @category Class
* @author WooCommerce
*/
defined( 'ABSPATH' ) || exit;
class WC_Subscriptions_Tracker {
/**
* Initialize the Tracker.
*/
public static function init() {
// Only add data if Tracker enabled
if ( 'yes' === get_option( 'woocommerce_allow_tracking', 'no' ) ) {
add_filter( 'woocommerce_tracker_data', array( __CLASS__, 'add_subscriptions_tracking_data' ), 10, 1 );
}
}
/**
* Adds Subscriptions data to the WC tracked data.
*
* @param array $data
* @return array all the tracking data.
*/
public static function add_subscriptions_tracking_data( $data ) {
$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();
return $data;
}
/**
* Gets the tracked Subscriptions options data.
*
* @return array Subscriptions options data.
*/
private static function get_subscriptions_options() {
return array(
// Staging and live site
'wc_subscriptions_staging' => WC_Subscriptions::is_duplicate_site() ? 'staging' : 'live',
'wc_subscriptions_live_url' => esc_url( WC_Subscriptions::get_site_url_from_source( 'subscriptions_install' ) ),
// Button text, roles
'add_to_cart_button_text' => get_option( WC_Subscriptions_Admin::$option_prefix . '_add_to_cart_button_text' ),
'order_button_text' => get_option( WC_Subscriptions_Admin::$option_prefix . '_order_button_text' ),
'subscriber_role' => get_option( WC_Subscriptions_Admin::$option_prefix . '_subscriber_role' ),
'cancelled_role' => get_option( WC_Subscriptions_Admin::$option_prefix . '_cancelled_role' ),
// Renewals
'accept_manual_renewals' => get_option( WC_Subscriptions_Admin::$option_prefix . '_accept_manual_renewals' ),
'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' ),
'enable_auto_renewal_toggle' => get_option( WC_Subscriptions_Admin::$option_prefix . '_enable_auto_renewal_toggle' ),
// Early renewal
'enable_early_renewal' => get_option( WC_Subscriptions_Admin::$option_prefix . '_enable_early_renewal' ),
'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
'allow_switching' => get_option( WC_Subscriptions_Admin::$option_prefix . '_allow_switching' ),
'apportion_recurring_price' => get_option( WC_Subscriptions_Admin::$option_prefix . '_apportion_recurring_price', 'none' ),
'apportion_sign_up_fee' => get_option( WC_Subscriptions_Admin::$option_prefix . '_apportion_sign_up_fee', 'none' ),
'apportion_length' => get_option( WC_Subscriptions_Admin::$option_prefix . '_apportion_length', 'none' ),
'switch_button_text' => get_option( WC_Subscriptions_Admin::$option_prefix . '_switch_button_text', 'none' ),
// Synchronization
'sync_payments' => get_option( WC_Subscriptions_Admin::$option_prefix . '_sync_payments' ),
'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' ) ),
'days_no_fee' => 'recurring' == $prorate_synced_payments ? get_option( WC_Subscriptions_Admin::$option_prefix . '_days_no_fee', 'none' ) : 'none',
// Miscellaneous
'max_customer_suspensions' => get_option( WC_Subscriptions_Admin::$option_prefix . '_max_customer_suspensions' ),
'multiple_purchase' => get_option( WC_Subscriptions_Admin::$option_prefix . '_multiple_purchase' ),
'allow_zero_initial_order_without_payment_method' => get_option( WC_Subscriptions_Admin::$option_prefix . '_zero_initial_payment_requires_payment' ),
'drip_downloadable_content_on_renewal' => get_option( WC_Subscriptions_Admin::$option_prefix . '_drip_downloadable_content_on_renewal' ),
'enable_retry' => get_option( WC_Subscriptions_Admin::$option_prefix . '_enable_retry' ),
);
}
/**
* Gets the combined subscription dates, count, and totals data.
*
* @return array
*/
private static function get_subscriptions() {
$subscription_dates = self::get_subscription_dates();
$subscription_counts = self::get_subscription_counts();
return array_merge( $subscription_dates, $subscription_counts );
}
/**
* Gets subscription counts.
*
* @return array
*/
private static function get_subscription_counts() {
$subscription_counts = array();
$subscription_counts_data = wp_count_posts( 'shop_subscription' );
foreach ( wcs_get_subscription_statuses() as $status_slug => $status_name ) {
$subscription_counts[ $status_slug ] = $subscription_counts_data->{ $status_slug };
}
return $subscription_counts;
}
/**
* Gets subscription order counts and totals.
*
* @return array
*/
private static function get_subscription_orders() {
global $wpdb;
$order_totals = array();
$relation_types = array(
'switch',
'renewal',
'resubscribe',
);
foreach ( $relation_types as $relation_type ) {
$total_and_count = $wpdb->get_row( sprintf(
"SELECT
SUM( order_total.meta_value ) AS 'gross_total', COUNT( orders.ID ) as 'count'
FROM {$wpdb->prefix}posts AS orders
LEFT JOIN {$wpdb->prefix}postmeta AS order_relation ON order_relation.post_id = orders.ID
LEFT JOIN {$wpdb->prefix}postmeta AS order_total ON order_total.post_id = orders.ID
WHERE order_relation.meta_key = '_subscription_%s'
AND orders.post_status in ( 'wc-completed', 'wc-processing', 'wc-refunded' )
AND order_total.meta_key = '_order_total'
GROUP BY order_total.meta_key
", $relation_type
), ARRAY_A );
$order_totals[ $relation_type . '_gross' ] = is_null( $total_and_count ) ? 0 : $total_and_count['gross_total'];
$order_totals[ $relation_type . '_count' ] = is_null( $total_and_count ) ? 0 : $total_and_count['count'];
}
// Finally get the initial revenue and count
$total_and_count = $wpdb->get_row(
"SELECT
SUM( order_total.meta_value ) AS 'gross_total', COUNT( * ) as 'count'
FROM {$wpdb->prefix}posts AS orders
LEFT JOIN {$wpdb->prefix}posts AS subscriptions ON subscriptions.post_parent = orders.ID
LEFT JOIN {$wpdb->prefix}postmeta AS order_total ON order_total.post_id = orders.ID
WHERE orders.post_status in ( 'wc-completed', 'wc-processing', 'wc-refunded' )
AND subscriptions.post_type = 'shop_subscription'
AND orders.post_type = 'shop_order'
AND order_total.meta_key = '_order_total'
GROUP BY order_total.meta_key
", ARRAY_A );
$initial_order_total = is_null( $total_and_count ) ? 0 : $total_and_count['gross_total'];
$initial_order_count = is_null( $total_and_count ) ? 0 : $total_and_count['count'];
// Don't double count resubscribe revenue and count
$order_totals['initial_gross'] = $initial_order_total - $order_totals['resubscribe_gross'];
$order_totals['initial_count'] = $initial_order_count - $order_totals['resubscribe_count'];
return $order_totals;
}
/**
* Gets first and last subscription created dates.
*
* @return array
*/
private static function get_subscription_dates() {
global $wpdb;
$min_max = $wpdb->get_row(
"
SELECT
MIN( post_date_gmt ) as 'first', MAX( post_date_gmt ) as 'last'
FROM {$wpdb->prefix}posts
WHERE post_type = 'shop_subscription'
AND post_status NOT IN ( 'trash', 'auto-draft' )
",
ARRAY_A
);
if ( is_null( $min_max ) ) {
$min_max = array(
'first' => '-',
'last' => '-',
);
}
return $min_max;
}
}

View File

@@ -1221,6 +1221,12 @@ class WCS_Cart_Renewal {
$cart_fees = $cart->get_fees(); $cart_fees = $cart->get_fees();
// Fees are naturally recurring if they have been applied to the renewal order. Generate a key (name + amount) for each fee applied to the order.
$renewal_order_fees = array();
foreach ( $this->get_order()->get_fees() as $item_id => $fee_line_item ) {
$renewal_order_fees[ $item_id ] = $fee_line_item->get_name() . wc_format_decimal( $fee_line_item->get_total() );
}
// WC doesn't have a method for removing fees individually so we clear them and re-add them where applicable. // WC doesn't have a method for removing fees individually so we clear them and re-add them where applicable.
if ( is_callable( array( $cart, 'fees_api' ) ) ) { // WC 3.2 + if ( is_callable( array( $cart, 'fees_api' ) ) ) { // WC 3.2 +
$cart->fees_api()->remove_all_fees(); $cart->fees_api()->remove_all_fees();
@@ -1229,7 +1235,10 @@ class WCS_Cart_Renewal {
} }
foreach ( $cart_fees as $fee ) { foreach ( $cart_fees as $fee ) {
if ( true === apply_filters( 'woocommerce_subscriptions_is_recurring_fee', false, $fee, $cart ) ) { // By default, a fee is automatically recurring if it was applied to the renewal order.
$is_recurring_fee = in_array( $fee->name . wc_format_decimal( $fee->amount ), $renewal_order_fees );
if ( true === apply_filters( 'woocommerce_subscriptions_is_recurring_fee', $is_recurring_fee, $fee, $cart ) ) {
if ( is_callable( array( $cart, 'fees_api' ) ) ) { // WC 3.2 + if ( is_callable( array( $cart, 'fees_api' ) ) ) { // WC 3.2 +
$cart->fees_api()->add_fee( $fee ); $cart->fees_api()->add_fee( $fee );
} else { } else {

View File

@@ -36,7 +36,7 @@ class WCS_Limiter {
public static function admin_edit_product_fields() { public static function admin_edit_product_fields() {
global $post; global $post;
echo '<div class="options_group limit_subscription show_if_subscription show_if_variable-subscription">'; echo '<div class="options_group limit_subscription show_if_subscription show_if_variable-subscription hidden">';
// Only one Subscription per customer // Only one Subscription per customer
woocommerce_wp_select( array( woocommerce_wp_select( array(

View File

@@ -66,13 +66,14 @@ class WCS_Modal {
return; return;
} }
$registered = true; $registered = true;
$enqueue_scripts_action = is_admin() ? 'admin_enqueue_scripts' : 'wp_enqueue_scripts';
// If the scripts are being registered late (after 'wp_enqueue_scripts' has run), it's safe to enqueue them immediately. // If the scripts are being registered late (after 'wp_enqueue_scripts' has run), it's safe to enqueue them immediately.
if ( did_action( 'wp_enqueue_scripts' ) ) { if ( did_action( $enqueue_scripts_action ) ) {
self::enqueue_scripts_and_styles(); self::enqueue_scripts_and_styles();
} else { } else {
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_scripts_and_styles' ) ); add_action( $enqueue_scripts_action, array( __CLASS__, 'enqueue_scripts_and_styles' ) );
} }
} }
@@ -83,7 +84,7 @@ class WCS_Modal {
*/ */
public static function enqueue_scripts_and_styles() { public static function enqueue_scripts_and_styles() {
wp_enqueue_script( 'wcs-modal-scripts', plugin_dir_url( WC_Subscriptions::$plugin_file ) . 'assets/js/modal.js', array( 'jquery' ), WC_Subscriptions::$version, true ); wp_enqueue_script( 'wcs-modal-scripts', plugin_dir_url( WC_Subscriptions::$plugin_file ) . 'assets/js/modal.js', array( 'jquery' ), WC_Subscriptions::$version, true );
wp_enqueue_style( 'wcs-modal-styles', plugin_dir_url( WC_Subscriptions::$plugin_file ) . 'assets/css/modal.css', array(), WC_Subscriptions::$version ); wp_enqueue_style( 'wcs-modal-styles', plugin_dir_url( WC_Subscriptions::$plugin_file ) . 'assets/css/modal.css', array( 'dashicons' ), WC_Subscriptions::$version );
} }
/** /**

View File

@@ -100,7 +100,7 @@ class WCS_My_Account_Payment_Methods {
// translators: $1: the token/credit card label, 2$-3$: opening and closing strong and link tags // translators: $1: the token/credit card label, 2$-3$: opening and closing strong and link tags
$notice = sprintf( esc_html__( 'The deleted payment method was used for automatic subscription payments. To avoid failed renewal payments in future the subscriptions using this payment method have been updated to use your %1$s. To change the payment method of individual subscriptions go to your %2$sMy Account > Subscriptions%3$s page.', 'woocommerce-subscriptions' ), $notice = sprintf( esc_html__( 'The deleted payment method was used for automatic subscription payments. To avoid failed renewal payments in future the subscriptions using this payment method have been updated to use your %1$s. To change the payment method of individual subscriptions go to your %2$sMy Account > Subscriptions%3$s page.', 'woocommerce-subscriptions' ),
self::get_token_label( $new_token ), $new_token->get_display_name(),
'<a href="' . esc_url( wc_get_account_endpoint_url( get_option( 'woocommerce_myaccount_subscriptions_endpoint', 'subscriptions' ) ) ) . '"><strong>', '<a href="' . esc_url( wc_get_account_endpoint_url( get_option( 'woocommerce_myaccount_subscriptions_endpoint', 'subscriptions' ) ) ) . '"><strong>',
'</strong></a>' '</strong></a>'
); );
@@ -111,19 +111,15 @@ class WCS_My_Account_Payment_Methods {
/** /**
* Get a WC_Payment_Token label. eg Visa ending in 1234 * Get a WC_Payment_Token label. eg Visa ending in 1234
* *
* @deprecated 2.7.2
*
* @param WC_Payment_Token payment token object * @param WC_Payment_Token payment token object
* @return string WC_Payment_Token label * @return string WC_Payment_Token label
* @since 2.2.7 * @since 2.2.7
*/ */
public static function get_token_label( $token ) { public static function get_token_label( $token ) {
wcs_deprecated_function( __METHOD__, '2.7.2', '$token->get_display_name()' );
if ( method_exists( $token, 'get_last4' ) && $token->get_last4() ) { return $token->get_display_name();
$label = sprintf( __( '%s ending in %s', 'woocommerce-subscriptions' ), esc_html( wc_get_credit_card_type_label( $token->get_card_type() ) ), esc_html( $token->get_last4() ) );
} else {
$label = esc_html( wc_get_credit_card_type_label( $token->get_card_type() ) );
}
return $label;
} }
/** /**
@@ -151,7 +147,7 @@ class WCS_My_Account_Payment_Methods {
} }
$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' ), $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' ),
self::get_token_label( $default_token ), $default_token->get_display_name(),
'</br><a href="' . esc_url( add_query_arg( array( '</br><a href="' . esc_url( add_query_arg( array(
'update-subscription-tokens' => 'true', 'update-subscription-tokens' => 'true',
'token-id' => $default_token_id, 'token-id' => $default_token_id,

View File

@@ -145,7 +145,7 @@ class WCS_Payment_Tokens extends WC_Payment_Tokens {
* @since 2.2.7 * @since 2.2.7
*/ */
public static function get_customers_alternative_token( $token ) { public static function get_customers_alternative_token( $token ) {
$payment_tokens = self::get_customer_tokens( $token->get_gateway_id(), $token->get_user_id() ); $payment_tokens = self::get_customer_tokens( $token->get_user_id(), $token->get_gateway_id() );
$alternative_token = null; $alternative_token = null;
// Remove the token we're trying to find an alternative for. // Remove the token we're trying to find an alternative for.

View File

@@ -166,9 +166,31 @@ class WCS_Switch_Totals_Calculator {
continue; continue;
} }
$switches[ $cart_item_key ] = new WCS_Switch_Cart_Item( $cart_item, $subscription, $existing_item ); $switch_item = new WCS_Switch_Cart_Item( $cart_item, $subscription, $existing_item );
} else { } else {
$switches[ $cart_item_key ] = new WCS_Add_Cart_Item( $cart_item, $subscription ); $switch_item = new WCS_Add_Cart_Item( $cart_item, $subscription );
}
/**
* Allow third-parties to filter the switch item and its properties.
*
* @since 2.7.2
*
* @param WCS_Switch_Cart_Item $switch_item The switch item.
* @param array $cart_item The item in the cart the switch item was created for.
* @param string $cart_item_key The cart item key.
*/
$switches[ $cart_item_key ] = apply_filters( 'wcs_proration_switch_item_from_cart_item', $switch_item, $cart_item, $cart_item_key );
// Ensure the filtered item is the correct object type.
if ( ! is_a( $switches[ $cart_item_key ], 'WCS_Switch_Cart_Item' ) ) {
unset( $switches[ $cart_item_key ] );
WC()->cart->remove_cart_item( $cart_item_key );
$error_notice = __( 'Your cart contained an invalid subscription switch request. It has been removed from your cart.', 'woocommerce-subscriptions' );
if ( ! wc_has_notice( $error_notice, 'error' ) ) {
wc_add_notice( $error_notice , 'error' );
}
} }
} }

View File

@@ -139,6 +139,7 @@ class WCS_Webhooks {
break; break;
case 'wp_api_v1': case 'wp_api_v1':
case 'wp_api_v2': case 'wp_api_v2':
case 'wp_api_v3':
$request = new WP_REST_Request( 'GET' ); $request = new WP_REST_Request( 'GET' );
$controller = new WC_REST_Subscriptions_Controller; $controller = new WC_REST_Subscriptions_Controller;

View File

@@ -125,10 +125,12 @@ class WCS_Early_Renewal_Modal_Handler {
// Now that we've attempted to process the payment, refresh the order. // Now that we've attempted to process the payment, refresh the order.
$renewal_order = wc_get_order( $renewal_order->get_id() ); $renewal_order = wc_get_order( $renewal_order->get_id() );
// Failed early renewals won't place the subscription on-hold so delete unsuccessful early renewal orders. // Failed early renewals won't place the subscription on-hold so delete unsuccessful early renewal orders and redirect the user to complete the payment via checkout.
if ( $renewal_order->needs_payment() ) { if ( $renewal_order->needs_payment() ) {
$renewal_order->delete( true ); $renewal_order->delete( true );
wc_add_notice( __( 'Payment for this renewal order was unsuccessful, please try again.', 'woocommerce-subscriptions' ), 'error' ); wc_add_notice( __( 'Payment for the renewal order was unsuccessful with your payment method on file, please try again.', 'woocommerce-subscriptions' ), 'error' );
wp_redirect( wcs_get_early_renewal_url( $subscription ) );
exit();
} else { } else {
wcs_update_dates_after_early_renewal( $subscription, $renewal_order ); wcs_update_dates_after_early_renewal( $subscription, $renewal_order );
wc_add_notice( __( 'Your early renewal order was successful.', 'woocommerce-subscriptions' ), 'success' ); wc_add_notice( __( 'Your early renewal order was successful.', 'woocommerce-subscriptions' ), 'success' );

View File

@@ -1,15 +0,0 @@
template: |
## next release &ndash; date
<!-- Move the individual changes below into the appropriate section -->
$CHANGES
**Added**
**Changed**
**Deprecated**
**Removed**
**Fixed**
**Security**
change-template: '* $TITLE (PR #$NUMBER)'

View File

@@ -1,11 +0,0 @@
{
"name": "prospress/action-scheduler",
"description": "Action Scheduler for WordPress and WooCommerce",
"type": "wordpress-plugin",
"license": "GPL-3.0",
"minimum-stability": "dev",
"require": {},
"require-dev": {
"wp-cli/wp-cli": "1.5.1"
}
}

View File

@@ -83,7 +83,7 @@ class WCS_Retry_Post_Store extends WCS_Retry_Store {
$retry_post = get_post( $retry_id ); $retry_post = get_post( $retry_id );
if ( null !== $retry_post ) { if ( null !== $retry_post && $retry_post->post_type === self::$post_type ) {
$rule_data = array(); $rule_data = array();
$post_meta = get_post_meta( $retry_id ); $post_meta = get_post_meta( $retry_id );

View File

@@ -654,7 +654,7 @@ function wcs_get_order_item_name( $order_item, $include = array() ) {
$meta_key = wc_attribute_label( wc_sanitize_taxonomy_name( $meta_key ) ); $meta_key = wc_attribute_label( wc_sanitize_taxonomy_name( $meta_key ) );
$meta_value = isset( $term->name ) ? $term->name : $meta_value; $meta_value = isset( $term->name ) ? $term->name : $meta_value;
} else { } else {
$meta_key = apply_filters( 'woocommerce_attribute_label', wc_attribute_label( $meta_key ), $meta_key ); $meta_key = wc_attribute_label( $meta_key );
} }
$attribute_strings[] = sprintf( '%s: %s', wp_kses_post( rawurldecode( $meta_key ) ), wp_kses_post( rawurldecode( $meta_value ) ) ); $attribute_strings[] = sprintf( '%s: %s', wp_kses_post( rawurldecode( $meta_key ) ), wp_kses_post( rawurldecode( $meta_value ) ) );
@@ -707,7 +707,7 @@ function wcs_get_line_item_name( $line_item ) {
$meta_key = wc_attribute_label( wc_sanitize_taxonomy_name( $meta_key ) ); $meta_key = wc_attribute_label( wc_sanitize_taxonomy_name( $meta_key ) );
$meta_value = isset( $term->name ) ? $term->name : $meta_value; $meta_value = isset( $term->name ) ? $term->name : $meta_value;
} else { } else {
$meta_key = apply_filters( 'woocommerce_attribute_label', wc_attribute_label( $meta_key ), $meta_key ); $meta_key = wc_attribute_label( $meta_key );
} }
$item_meta_strings[] = sprintf( '%s: %s', rawurldecode( $meta_key ), rawurldecode( $meta_value ) ); $item_meta_strings[] = sprintf( '%s: %s', rawurldecode( $meta_key ), rawurldecode( $meta_value ) );

File diff suppressed because it is too large Load Diff

View File

@@ -33,12 +33,13 @@ global $wp_locale;
<?php echo esc_html( WC_Subscriptions_Synchroniser::$sync_field_label ); ?> <?php echo esc_html( WC_Subscriptions_Synchroniser::$sync_field_label ); ?>
<?php echo wcs_help_tip( WC_Subscriptions_Synchroniser::$sync_description_year ); ?> <?php echo wcs_help_tip( WC_Subscriptions_Synchroniser::$sync_description_year ); ?>
</label> </label>
<input type="number" class="wc_input_subscription_payment_sync wc_input_subscription_payment_sync_day" name="variable_subscription_payment_sync_date_day[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( $payment_day ); ?>" placeholder="<?php echo esc_attr_x( 'Day', 'input field placeholder for day field for annual subscriptions', 'woocommerce-subscriptions' ); ?>" step="1" min="0" max="31">
<select name="variable_subscription_payment_sync_date_month[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_payment_sync wc_input_subscription_payment_sync_month"> <select name="variable_subscription_payment_sync_date_month[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_payment_sync wc_input_subscription_payment_sync_month">
<?php foreach ( $wp_locale->month as $key => $value ) : ?> <?php foreach ( WC_Subscriptions_Synchroniser::get_year_sync_options() as $key => $value ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, $payment_month ); ?>><?php echo esc_html( $value ); ?></option> <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, $payment_month ); ?>><?php echo esc_html( $value ); ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<?php $daysInMonth = $payment_month ? cal_days_in_month( CAL_GREGORIAN, (int) $payment_month, 2001 ) : 0; ?>
<input type="number" class="wc_input_subscription_payment_sync wc_input_subscription_payment_sync_day" name="variable_subscription_payment_sync_date_day[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( $payment_day ); ?>" placeholder="<?php echo esc_attr_x( 'Day', 'input field placeholder for day field for annual subscriptions', 'woocommerce-subscriptions' ); ?>" step="1" min="<?php echo esc_attr( min( 1, $daysInMonth ) ); ?>" max="<?php echo esc_attr( $daysInMonth ); ?>" <?php disabled( 0, $payment_month, true ); ?> />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -15,7 +15,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<?php if ( $modal->has_heading() ) : ?> <?php if ( $modal->has_heading() ) : ?>
<h2><?php echo esc_html( $modal->get_heading() ) ?></h2> <h2><?php echo esc_html( $modal->get_heading() ) ?></h2>
<?php endif ?> <?php endif ?>
<a href="#" onclick="return false;" type="button" class="close" style="text-decoration: none;"><i class="fas fa-times"></i></a> <a href="#" onclick="return false;" class="close" style="text-decoration: none;"><span class="dashicons dashicons-no"></span></a>
</header> </header>
<div class="content"> <div class="content">

View File

@@ -4,7 +4,7 @@
* *
* @author Prospress * @author Prospress
* @category WooCommerce Subscriptions/Templates * @category WooCommerce Subscriptions/Templates
* @version 2.6.0 * @version 2.6.4
*/ */
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
@@ -71,9 +71,13 @@ if ( ! defined( 'ABSPATH' ) ) {
<?php if ( 1 < $current_page ) : <?php if ( 1 < $current_page ) :
printf( esc_html__( 'You have reached the end of subscriptions. Go to the %sfirst page%s.', 'woocommerce-subscriptions' ), '<a href="' . esc_url( wc_get_endpoint_url( 'subscriptions', 1 ) ) . '">', '</a>' ); printf( esc_html__( 'You have reached the end of subscriptions. Go to the %sfirst page%s.', 'woocommerce-subscriptions' ), '<a href="' . esc_url( wc_get_endpoint_url( 'subscriptions', 1 ) ) . '">', '</a>' );
else : else :
// translators: placeholders are opening and closing link tags to take to the shop page esc_html_e( 'You have no active subscriptions.', 'woocommerce-subscriptions' );
printf( esc_html__( 'You have no active subscriptions. Find your first subscription in the %sstore%s.', 'woocommerce-subscriptions' ), '<a href="' . esc_url( apply_filters( 'woocommerce_subscriptions_message_store_url', get_permalink( wc_get_page_id( 'shop' ) ) ) ) . '">', '</a>' ); ?>
endif; ?> <a class="woocommerce-Button button" href="<?php echo esc_url( apply_filters( 'woocommerce_return_to_shop_redirect', wc_get_page_permalink( 'shop' ) ) ); ?>">
<?php esc_html_e( 'Browse products', 'woocommerce-subscriptions' ); ?>
</a>
<?php
endif; ?>
</p> </p>
<?php endif; ?> <?php endif; ?>

View File

@@ -4,7 +4,7 @@
* *
* @author Prospress * @author Prospress
* @category WooCommerce Subscriptions/Templates * @category WooCommerce Subscriptions/Templates
* @version 2.6.0 * @version 2.6.2
*/ */
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
@@ -36,7 +36,7 @@ if ( ! defined( 'ABSPATH' ) ) {
} }
$item_count = $order->get_item_count(); $item_count = $order->get_item_count();
$order_date = wcs_get_datetime_utc_string( $order->get_date_created() ); $order_date = $order->get_date_created();
?><tr class="order woocommerce-orders-table__row woocommerce-orders-table__row--status-<?php echo esc_attr( $order->get_status() ); ?>"> ?><tr class="order woocommerce-orders-table__row woocommerce-orders-table__row--status-<?php echo esc_attr( $order->get_status() ); ?>">
<td class="order-number woocommerce-orders-table__cell woocommerce-orders-table__cell-order-number" data-title="<?php esc_attr_e( 'Order Number', 'woocommerce-subscriptions' ); ?>"> <td class="order-number woocommerce-orders-table__cell woocommerce-orders-table__cell-order-number" data-title="<?php esc_attr_e( 'Order Number', 'woocommerce-subscriptions' ); ?>">
@@ -45,7 +45,7 @@ if ( ! defined( 'ABSPATH' ) ) {
</a> </a>
</td> </td>
<td class="order-date woocommerce-orders-table__cell woocommerce-orders-table__cell-order-date" data-title="<?php esc_attr_e( 'Date', 'woocommerce-subscriptions' ); ?>"> <td class="order-date woocommerce-orders-table__cell woocommerce-orders-table__cell-order-date" data-title="<?php esc_attr_e( 'Date', 'woocommerce-subscriptions' ); ?>">
<time datetime="<?php echo esc_attr( gmdate( 'Y-m-d', wcs_date_to_time( $order_date ) ) ); ?>" title="<?php echo esc_attr( wcs_date_to_time( $order_date ) ); ?>"><?php echo wp_kses_post( date_i18n( get_option( 'date_format' ), wcs_date_to_time( $order_date ) ) ); ?></time> <time datetime="<?php echo esc_attr( $order_date->date( 'Y-m-d' ) ); ?>" title="<?php echo esc_attr( $order_date->getTimestamp() ); ?>"><?php echo wp_kses_post( $order_date->date_i18n( wc_date_format() ) ); ?></time>
</td> </td>
<td class="order-status woocommerce-orders-table__cell woocommerce-orders-table__cell-order-status" data-title="<?php esc_attr_e( 'Status', 'woocommerce-subscriptions' ); ?>" style="white-space:nowrap;"> <td class="order-status woocommerce-orders-table__cell woocommerce-orders-table__cell-order-status" data-title="<?php esc_attr_e( 'Status', 'woocommerce-subscriptions' ); ?>" style="white-space:nowrap;">
<?php echo esc_html( wc_get_order_status_name( $order->get_status() ) ); ?> <?php echo esc_html( wc_get_order_status_name( $order->get_status() ) ); ?>

View File

@@ -5,7 +5,7 @@
* @author Prospress * @author Prospress
* @package WooCommerce_Subscription/Templates * @package WooCommerce_Subscription/Templates
* @since 2.2.19 * @since 2.2.19
* @version 2.6.0 * @version 2.6.5
*/ */
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
@@ -18,18 +18,16 @@ if ( ! defined( 'ABSPATH' ) ) {
<td><?php esc_html_e( 'Status', 'woocommerce-subscriptions' ); ?></td> <td><?php esc_html_e( 'Status', 'woocommerce-subscriptions' ); ?></td>
<td><?php echo esc_html( wcs_get_subscription_status_name( $subscription->get_status() ) ); ?></td> <td><?php echo esc_html( wcs_get_subscription_status_name( $subscription->get_status() ) ); ?></td>
</tr> </tr>
<tr> <?php do_action( 'wcs_subscription_details_table_before_dates', $subscription ); ?>
<td><?php echo esc_html_x( 'Start date', 'table heading', 'woocommerce-subscriptions' ); ?></td> <?php
<td><?php echo esc_html( $subscription->get_date_to_display( 'start_date' ) ); ?></td> $dates_to_display = apply_filters( 'wcs_subscription_details_table_dates_to_display', array(
</tr> 'start_date' => _x( 'Start date', 'customer subscription table header', 'woocommerce-subscriptions' ),
<?php foreach ( 'last_order_date_created' => _x( 'Last order date', 'customer subscription table header', 'woocommerce-subscriptions' ),
array( 'next_payment' => _x( 'Next payment date', 'customer subscription table header', 'woocommerce-subscriptions' ),
'last_order_date_created' => _x( 'Last order date', 'admin subscription table header', 'woocommerce-subscriptions' ), 'end' => _x( 'End date', 'customer subscription table header', 'woocommerce-subscriptions' ),
'next_payment' => _x( 'Next payment date', 'admin subscription table header', 'woocommerce-subscriptions' ), 'trial_end' => _x( 'Trial end date', 'customer subscription table header', 'woocommerce-subscriptions' ),
'end' => _x( 'End date', 'table heading', 'woocommerce-subscriptions' ), ), $subscription );
'trial_end' => _x( 'Trial end date', 'admin subscription table header', 'woocommerce-subscriptions' ), foreach ( $dates_to_display as $date_type => $date_title ) : ?>
) as $date_type => $date_title
) : ?>
<?php $date = $subscription->get_date( $date_type ); ?> <?php $date = $subscription->get_date( $date_type ); ?>
<?php if ( ! empty( $date ) ) : ?> <?php if ( ! empty( $date ) ) : ?>
<tr> <tr>
@@ -38,6 +36,7 @@ if ( ! defined( 'ABSPATH' ) ) {
</tr> </tr>
<?php endif; ?> <?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
<?php do_action( 'wcs_subscription_details_table_after_dates', $subscription ); ?>
<?php if ( WCS_My_Account_Auto_Renew_Toggle::can_subscription_auto_renewal_be_changed( $subscription ) ) : ?> <?php if ( WCS_My_Account_Auto_Renew_Toggle::can_subscription_auto_renewal_be_changed( $subscription ) ) : ?>
<tr> <tr>
<td><?php esc_html_e( 'Auto renew', 'woocommerce-subscriptions' ); ?></td> <td><?php esc_html_e( 'Auto renew', 'woocommerce-subscriptions' ); ?></td>
@@ -66,6 +65,7 @@ if ( ! defined( 'ABSPATH' ) ) {
</td> </td>
</tr> </tr>
<?php endif; ?> <?php endif; ?>
<?php do_action( 'wcs_subscription_details_table_before_payment_method', $subscription ); ?>
<?php if ( $subscription->get_time( 'next_payment' ) > 0 ) : ?> <?php if ( $subscription->get_time( 'next_payment' ) > 0 ) : ?>
<tr> <tr>
<td><?php esc_html_e( 'Payment', 'woocommerce-subscriptions' ); ?></td> <td><?php esc_html_e( 'Payment', 'woocommerce-subscriptions' ); ?></td>
@@ -91,7 +91,7 @@ if ( ! defined( 'ABSPATH' ) ) {
</table> </table>
<?php if ( $notes = $subscription->get_customer_order_notes() ) : ?> <?php if ( $notes = $subscription->get_customer_order_notes() ) : ?>
<h2><?php esc_html_e( 'Subscription Updates', 'woocommerce-subscriptions' ); ?></h2> <h2><?php esc_html_e( 'Subscription updates', 'woocommerce-subscriptions' ); ?></h2>
<ol class="woocommerce-OrderUpdates commentlist notes"> <ol class="woocommerce-OrderUpdates commentlist notes">
<?php foreach ( $notes as $note ) : ?> <?php foreach ( $notes as $note ) : ?>
<li class="woocommerce-OrderUpdate comment note"> <li class="woocommerce-OrderUpdate comment note">

View File

@@ -73,7 +73,7 @@ function wcs_do_subscriptions_exist() {
* *
* @since 2.0 * @since 2.0
* @param mixed $the_subscription Post object or post ID of the order. * @param mixed $the_subscription Post object or post ID of the order.
* @return WC_Subscription * @return WC_Subscription|false The subscription object, or false if it cannot be found.
*/ */
function wcs_get_subscription( $the_subscription ) { function wcs_get_subscription( $the_subscription ) {

View File

@@ -5,10 +5,10 @@
* Description: Sell products and services with recurring payments in your WooCommerce Store. * Description: Sell products and services with recurring payments in your WooCommerce Store.
* Author: Automattic * Author: Automattic
* Author URI: https://woocommerce.com/ * Author URI: https://woocommerce.com/
* Version: 2.6.1 * Version: 2.6.5
* *
* WC requires at least: 3.0 * WC requires at least: 3.0.9
* WC tested up to: 3.6 * WC tested up to: 3.8
* Woo: 27147:6115e6d7e297b623a169fdcf5728b224 * Woo: 27147:6115e6d7e297b623a169fdcf5728b224
* *
* Copyright 2019 WooCommerce * Copyright 2019 WooCommerce
@@ -83,6 +83,7 @@ WC_Subscriptions_Change_Payment_Gateway::init();
WC_Subscriptions_Payment_Gateways::init(); WC_Subscriptions_Payment_Gateways::init();
WCS_PayPal_Standard_Change_Payment_Method::init(); WCS_PayPal_Standard_Change_Payment_Method::init();
WC_Subscriptions_Switcher::init(); WC_Subscriptions_Switcher::init();
WC_Subscriptions_Tracker::init();
WCS_Upgrade_Logger::init(); WCS_Upgrade_Logger::init();
new WCS_Cart_Renewal(); new WCS_Cart_Renewal();
new WCS_Cart_Resubscribe(); new WCS_Cart_Resubscribe();
@@ -117,7 +118,7 @@ class WC_Subscriptions {
public static $plugin_file = __FILE__; public static $plugin_file = __FILE__;
public static $version = '2.6.1'; public static $version = '2.6.5';
public static $wc_minimum_supported_version = '3.0'; public static $wc_minimum_supported_version = '3.0';
@@ -740,7 +741,7 @@ class WC_Subscriptions {
update_option( WC_Subscriptions_admin::$option_prefix . '_paypal_debugging_default_set', 'true' ); update_option( WC_Subscriptions_admin::$option_prefix . '_paypal_debugging_default_set', 'true' );
} }
add_option( WC_Subscriptions_Admin::$option_prefix . '_is_active', true ); update_option( WC_Subscriptions_Admin::$option_prefix . '_is_active', true );
set_transient( self::$activation_transient, true, 60 * 60 ); set_transient( self::$activation_transient, true, 60 * 60 );
@@ -894,7 +895,7 @@ class WC_Subscriptions {
// translators: 1$-2$: opening and closing <strong> tags. 3$-4$: opening and closing link tags for learn more. Leads to duplicate site article on docs. 5$-6$: Opening and closing link to production URL. 7$: Production URL . // translators: 1$-2$: opening and closing <strong> tags. 3$-4$: opening and closing link tags for learn more. Leads to duplicate site article on docs. 5$-6$: Opening and closing link to production URL. 7$: Production URL .
esc_html__( 'It looks like this site has moved or is a duplicate site. %1$sWooCommerce Subscriptions%2$s has disabled automatic payments and subscription related emails on this site to prevent duplicate payments from a staging or test environment. %1$sWooCommerce Subscriptions%2$s considers %5$s%7$s%6$s to be the site\'s URL. %3$sLearn more &raquo;%4$s.', 'woocommerce-subscriptions' ), esc_html__( 'It looks like this site has moved or is a duplicate site. %1$sWooCommerce Subscriptions%2$s has disabled automatic payments and subscription related emails on this site to prevent duplicate payments from a staging or test environment. %1$sWooCommerce Subscriptions%2$s considers %5$s%7$s%6$s to be the site\'s URL. %3$sLearn more &raquo;%4$s.', 'woocommerce-subscriptions' ),
'<strong>', '</strong>', '<strong>', '</strong>',
'<a href="http://docs.woocommerce.com/document/subscriptions/faq/#section-39" target="_blank">', '</a>', '<a href="https://docs.woocommerce.com/document/subscriptions-handles-staging-sites/" target="_blank">', '</a>',
'<a href="' . esc_url( self::get_site_url_from_source( 'subscriptions_install' ) ) . '" target="_blank">', '</a>', '<a href="' . esc_url( self::get_site_url_from_source( 'subscriptions_install' ) ) . '" target="_blank">', '</a>',
esc_url( self::get_site_url_from_source( 'subscriptions_install' ) ) esc_url( self::get_site_url_from_source( 'subscriptions_install' ) )
) )