mirror of
https://github.com/pronamic/woocommerce-subscriptions.git
synced 2025-10-08 10:34:03 +00:00
2.0.19
This commit is contained in:
@@ -266,6 +266,125 @@ jQuery(document).ready(function($){
|
||||
|
||||
return value;
|
||||
},
|
||||
disableEnableOneTimeShipping: function() {
|
||||
var is_synced_or_has_trial = false;
|
||||
|
||||
if ( 'variable-subscription' == $( 'select#product-type' ).val() ) {
|
||||
var variations = $( '.woocommerce_variations .woocommerce_variation' ),
|
||||
variations_checked = {},
|
||||
number_of_pages = $( '.woocommerce_variations' ).attr( 'data-total_pages' );
|
||||
|
||||
$(variations).each(function() {
|
||||
var period_field = $( this ).find( '.wc_input_subscription_period' ),
|
||||
variation_index = $( period_field ).attr( 'name' ).match(/\[(.*?)\]/),
|
||||
variation_id = $( '[name="variable_post_id['+variation_index[1]+']"]' ).val(),
|
||||
period = period_field.val(),
|
||||
trial = $( this ).find( '.wc_input_subscription_trial_length' ).val(),
|
||||
sync_date = 0;
|
||||
|
||||
if ( 0 != trial ) {
|
||||
is_synced_or_has_trial = true;
|
||||
|
||||
// break
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $( this ).find( '.variable_subscription_sync' ).length ) {
|
||||
if ( 'month' == period || 'week' == period ) {
|
||||
sync_date = $( '[name="variable_subscription_payment_sync_date['+variation_index[1]+']"]' ).val();
|
||||
} else if ( 'year' == period ) {
|
||||
sync_date = $( '[name="variable_subscription_payment_sync_date_day['+variation_index[1]+']"]' ).val();
|
||||
}
|
||||
|
||||
if ( 0 != sync_date ) {
|
||||
is_synced_or_has_trial = true;
|
||||
|
||||
// break
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
variations_checked[ variation_index[1] ] = variation_id;
|
||||
});
|
||||
|
||||
// if we haven't found a variation synced or with a trial at this point check the backend for other product variations
|
||||
if ( ( number_of_pages > 1 || 0 == variations.size() ) && false == is_synced_or_has_trial ) {
|
||||
|
||||
var data = {
|
||||
action: 'wcs_product_has_trial_or_is_synced',
|
||||
product_id: woocommerce_admin_meta_boxes_variations.post_id,
|
||||
variations_checked: variations_checked,
|
||||
nonce: WCSubscriptions.oneTimeShippingCheckNonce,
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: WCSubscriptions.ajaxUrl,
|
||||
data: data,
|
||||
type: 'POST',
|
||||
success : function( response ) {
|
||||
$( '#_subscription_one_time_shipping' ).prop( 'disabled', response.is_synced_or_has_trial );
|
||||
// trigger an event now we have determined the one time shipping availability, in case we need to update the backend
|
||||
$( '#_subscription_one_time_shipping' ).trigger( 'subscription_one_time_shipping_updated', [ response.is_synced_or_has_trial ] );
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// trigger an event now we have determined the one time shipping availability, in case we need to update the backend
|
||||
$( '#_subscription_one_time_shipping' ).trigger( 'subscription_one_time_shipping_updated', [ is_synced_or_has_trial ] );
|
||||
}
|
||||
} else {
|
||||
var trial = $( '#general_product_data #_subscription_trial_length' ).val();
|
||||
|
||||
if ( 0 != trial ) {
|
||||
is_synced_or_has_trial = true;
|
||||
}
|
||||
|
||||
if ( $( '.subscription_sync' ).length && false == is_synced_or_has_trial ) {
|
||||
var period = $( '#_subscription_period' ).val(),
|
||||
sync_date = 0;
|
||||
|
||||
if ( 'month' == period || 'week' == period ) {
|
||||
sync_date = $( '#_subscription_payment_sync_date' ).val();
|
||||
} else if ( 'year' == period ) {
|
||||
sync_date = $( '#_subscription_payment_sync_date_day' ).val();
|
||||
}
|
||||
|
||||
if ( 0 != sync_date ) {
|
||||
is_synced_or_has_trial = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$( '#_subscription_one_time_shipping' ).prop( 'disabled', is_synced_or_has_trial );
|
||||
},
|
||||
show_hidden_panels: function() {
|
||||
// WooCommerce's show_hidden_panels() function introduced a change in WC 2.6.2 which would hide the general panel for variable subscriptions, this fixes that by running the show_hidden_panels() logic again after variable fields have been show/hidden for variable subscriptions
|
||||
$( '.woocommerce_options_panel' ).each( function() {
|
||||
|
||||
if ( 'none' != $( this ).css( 'display' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $children = $( this ).children( '.options_group' );
|
||||
|
||||
if ( 0 === $children.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $invisble = $children.filter( function() {
|
||||
return 'none' === $( this ).css( 'display' );
|
||||
});
|
||||
|
||||
// Show panel if it's been mistakenly hidden panel
|
||||
if ( $invisble.length != $children.length ) {
|
||||
var $id = $( this ).prop( 'id' );
|
||||
$( '.product_data_tabs' ).find( 'li a[href="#' + $id + '"]' ).parent().show();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Now select the first panel in case it's changed
|
||||
$( 'ul.wc-tabs li:visible' ).eq( 0 ).find( 'a' ).click();
|
||||
},
|
||||
});
|
||||
|
||||
$('.options_group.pricing ._sale_price_field .description').prepend('<span id="sale-price-period" style="display: none;"></span>');
|
||||
@@ -293,6 +412,8 @@ jQuery(document).ready(function($){
|
||||
$.setSubscriptionLengths();
|
||||
$.setTrialPeriods();
|
||||
$.showHideSyncOptions();
|
||||
$.disableEnableOneTimeShipping();
|
||||
$.show_hidden_panels();
|
||||
}
|
||||
|
||||
// Update subscription ranges when subscription period or interval is changed
|
||||
@@ -301,6 +422,7 @@ jQuery(document).ready(function($){
|
||||
$.showHideSyncOptions();
|
||||
$.setSyncOptions( $(this) );
|
||||
$.setSalePeriod();
|
||||
$.disableEnableOneTimeShipping();
|
||||
});
|
||||
|
||||
$('#woocommerce-product-data').on('propertychange keyup input paste change','[name^="_subscription_trial_length"], [name^="variable_subscription_trial_length"]',function(){
|
||||
@@ -311,6 +433,7 @@ jQuery(document).ready(function($){
|
||||
$.showHideSubscriptionMeta();
|
||||
$.showHideVariableSubscriptionMeta();
|
||||
$.showHideSyncOptions();
|
||||
$.show_hidden_panels();
|
||||
});
|
||||
|
||||
$('input#_downloadable, input#_virtual').change(function(){
|
||||
@@ -406,7 +529,14 @@ jQuery(document).ready(function($){
|
||||
|
||||
// WC 2.4+ variation bulk edit handling
|
||||
$('select.variation_actions').on('variable_subscription_sign_up_fee_ajax_data variable_subscription_period_interval_ajax_data variable_subscription_period_ajax_data variable_subscription_trial_period_ajax_data variable_subscription_trial_length_ajax_data variable_subscription_length_ajax_data', function(event, data) {
|
||||
value = $.getVariationBulkEditValue(event.type.replace(/_ajax_data/g,''));
|
||||
bulk_action = event.type.replace(/_ajax_data/g,'');
|
||||
value = $.getVariationBulkEditValue( bulk_action );
|
||||
|
||||
if ( 'variable_subscription_trial_length' == bulk_action ) {
|
||||
// After variations have their trial length bulk updated in the backend, flag the One Time Shipping field as needing to be updated
|
||||
$( '#_subscription_one_time_shipping' ).addClass( 'wcs_ots_needs_update' );
|
||||
}
|
||||
|
||||
if ( value != null ) {
|
||||
data.value = value;
|
||||
}
|
||||
@@ -487,4 +617,45 @@ jQuery(document).ready(function($){
|
||||
$('#wcs_' + payment_method + '_fields').show();
|
||||
});
|
||||
|
||||
// After variations have been saved/updated in the backend, flag the One Time Shipping field as needing to be updated
|
||||
$( '#woocommerce-product-data' ).on( 'woocommerce_variations_saved', function() {
|
||||
$( '#_subscription_one_time_shipping' ).addClass( 'wcs_ots_needs_update' );
|
||||
});
|
||||
|
||||
// After variations have been loaded and if the One Time Shipping field needs updating, check if One Time Shipping is still available
|
||||
$( '#woocommerce-product-data' ).on( 'woocommerce_variations_loaded', function() {
|
||||
if ( $( '.wcs_ots_needs_update' ).length ) {
|
||||
$.disableEnableOneTimeShipping();
|
||||
}
|
||||
});
|
||||
|
||||
// Triggered by $.disableEnableOneTimeShipping() after One Time shipping has been enabled or disabled for variations.
|
||||
// If the One Time Shipping field needs updating, send the ajax request to update the product setting in the backend
|
||||
$( '#_subscription_one_time_shipping' ).on( 'subscription_one_time_shipping_updated', function( event, is_synced_or_has_trial ) {
|
||||
|
||||
if ( $( '.wcs_ots_needs_update' ).length ) {
|
||||
var data = {
|
||||
action: 'wcs_update_one_time_shipping',
|
||||
product_id: woocommerce_admin_meta_boxes_variations.post_id,
|
||||
one_time_shipping_enabled: ! is_synced_or_has_trial,
|
||||
one_time_shipping_selected: $( '#_subscription_one_time_shipping' ).prop( 'checked' ),
|
||||
nonce: WCSubscriptions.oneTimeShippingCheckNonce,
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: WCSubscriptions.ajaxUrl,
|
||||
data: data,
|
||||
type: 'POST',
|
||||
success : function( response ) {
|
||||
// remove the flag requiring the one time shipping field to be updated
|
||||
$( '#_subscription_one_time_shipping' ).removeClass( 'wcs_ots_needs_update' );
|
||||
$( '#_subscription_one_time_shipping' ).prop( 'checked', response.one_time_shipping == 'yes' ? true : false );
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$( '#general_product_data, #variable_product_options' ).on( 'change', '[class^="wc_input_subscription_payment_sync"], [class^="wc_input_subscription_trial_length"]', function() {
|
||||
$.disableEnableOneTimeShipping();
|
||||
});
|
||||
});
|
||||
|
@@ -1,5 +1,18 @@
|
||||
*** WooCommerce Subscriptions Changelog ***
|
||||
|
||||
2016.08.12 - version 2.0.19
|
||||
* Tweak: require new manually added subscriptions to have a customer to avoid issues with subscriptions created without a customer. (PR#1486)
|
||||
* Tweak: set expiration on caching transients to avoid them being autoloaded on every request and unnecessarily using memory on those requests. (PR#1540)
|
||||
* Fix: update Action Scheduler to apply timezone fixes and avoid dupilcate payments when a store's timezone is changed: https://github.com/Prospress/action-scheduler/pull/43 (PR#1569)
|
||||
* Fix: incompatibility with WooCommerce 2.6.2+ which prevented the General tab on the WooCommerce > Edit Product administration screen from being displayed correctly. (PR#1571)
|
||||
* Fix: shipping rate caching introduced in v2.0.18 for carts containing only products which are synchronised or have a free trial. (PR#1584)
|
||||
* Fix: displaly appropriate notice when there is no recurring shipping method available because the customer's location has not been set. (PR#1579)
|
||||
* Fix: page title conflict with bbPress subscription pages. (PR#1581)
|
||||
* Fix: undefined index error when using variation bulk actions (PR#1561)
|
||||
* Fix: redirect loop on product page when auto-switching a limited subscription that is on-hold. (PR#1557)
|
||||
* Fix: issues on WP Engine and potentially other hosts when filtering orders displayed on the WooCoommerce > Orders administration screen to only show renewal orders as a result of WP Engine limiting query length. (PR#1555)
|
||||
* Fix: undefined variable notice when checking if a product is limited. (PR#1552)
|
||||
|
||||
2016.07.22 - version 2.0.18
|
||||
* Tweak: cache shipping package rates to prevent multiple re-calculations per request. (PR#1485)
|
||||
* Tweak: remove multiple, redundant calls to $subscription->get_items() and WP_Posts_List->__construct() on admin subscriptions screen. (PR#1524)
|
||||
|
@@ -43,7 +43,7 @@ abstract class WCS_Cache_Manager {
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function cache_and_get( $key, $callback, $params = array(), $expires = 0 );
|
||||
abstract public function cache_and_get( $key, $callback, $params = array(), $expires = WEEK_IN_SECONDS );
|
||||
|
||||
/**
|
||||
* Deletes a cached version of data.
|
||||
|
@@ -77,6 +77,8 @@ class WC_Subscriptions_Admin {
|
||||
add_action( 'woocommerce_process_product_meta_variable-subscription', __CLASS__ . '::process_product_meta_variable_subscription' ); // WC < 2.4
|
||||
add_action( 'woocommerce_ajax_save_product_variations', __CLASS__ . '::process_product_meta_variable_subscription' );
|
||||
|
||||
add_action( 'woocommerce_subscription_pre_update_status', __CLASS__ . '::check_customer_is_set', 10, 3 );
|
||||
|
||||
add_action( 'product_variation_linked', __CLASS__ . '::set_variation_meta_defaults_on_bulk_add' );
|
||||
|
||||
add_filter( 'woocommerce_settings_tabs_array', __CLASS__ . '::add_subscription_settings_tab', 50 );
|
||||
@@ -242,7 +244,7 @@ class WC_Subscriptions_Admin {
|
||||
woocommerce_wp_checkbox( array(
|
||||
'id' => '_subscription_one_time_shipping',
|
||||
'label' => __( 'One Time Shipping', 'woocommerce-subscriptions' ),
|
||||
'description' => __( 'Shipping for subscription products is normally charged on the initial order and all renewal orders. Enable this to only charge shipping once on the initial order. Note: for shipping to be charged on the initial order, the subscription must not have a free trial.', 'woocommerce-subscriptions' ),
|
||||
'description' => __( 'Shipping for subscription products is normally charged on the initial order and all renewal orders. Enable this to only charge shipping once on the initial order. Note: for this setting to be enabled the subscription must not have a free trial or a synced renewal date.', 'woocommerce-subscriptions' ),
|
||||
'desc_tip' => true,
|
||||
) );
|
||||
|
||||
@@ -620,6 +622,36 @@ class WC_Subscriptions_Admin {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure when saving a subscription via the admin to activate it, it has a valid customer set on it.
|
||||
*
|
||||
* When you click "Add New Subscription", the status is already going to be pending to begin with. This will prevent
|
||||
* changing the status to anything else besides pending if no customer is specified, or the customer specified is
|
||||
* not a valid WP_User.
|
||||
*
|
||||
* Hooked into `woocommerce_subscription_pre_update_status`
|
||||
*
|
||||
* @param string $old_status Previous status of the subscription in update_status
|
||||
* @param string $new_status New status of the subscription in update_status
|
||||
* @param WC_Subscription $subscription The subscription being saved
|
||||
*
|
||||
* @return null
|
||||
* @throws Exception in case there was no user found / there's no customer attached to it
|
||||
*/
|
||||
public static function check_customer_is_set( $old_status, $new_status, $subscription ) {
|
||||
global $post;
|
||||
|
||||
if ( is_admin() && 'active' == $new_status && ! empty( $post ) && 'shop_subscription' === $post->post_type ) {
|
||||
|
||||
$customer_user = ( isset( $_POST['customer_user'] ) ) ? sanitize_text_field( $_POST['customer_user'] ) : ''; // csrf in core wp save post function
|
||||
$user = new WP_User( $customer_user );
|
||||
|
||||
if ( 0 === $user->ID ) {
|
||||
throw new Exception( sprintf( __( 'Unable to change subscription status to "%s". Please assign a customer to the subscription to activate it.', 'woocommerce-subscriptions' ), $new_status ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default values for subscription dropdown fields when bulk adding variations to fix issue #1342
|
||||
*
|
||||
@@ -672,6 +704,7 @@ class WC_Subscriptions_Admin {
|
||||
'bulkEditPeriodMessage' => __( 'Enter the new period, either day, week, month or year:', 'woocommerce-subscriptions' ),
|
||||
'bulkEditLengthMessage' => __( 'Enter a new length (e.g. 5):', 'woocommerce-subscriptions' ),
|
||||
'bulkEditIntervalhMessage' => __( 'Enter a new interval as a single number (e.g. to charge every 2nd month, enter 2):', 'woocommerce-subscriptions' ),
|
||||
'oneTimeShippingCheckNonce' => wp_create_nonce( 'one_time_shipping' ),
|
||||
);
|
||||
} else if ( 'edit-shop_order' == $screen->id ) {
|
||||
$script_params = array(
|
||||
|
@@ -272,7 +272,7 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
// translators: placeholder is error message from the payment gateway or subscriptions when updating the status
|
||||
wcs_add_admin_notice( sprintf( __( 'Error updating subscription: %s', 'woocommerce-subscriptions' ), $e->getMessage() ), 'error' );
|
||||
wcs_add_admin_notice( sprintf( __( 'Error updating some information: %s', 'woocommerce-subscriptions' ), $e->getMessage() ), 'error' );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_process_shop_subscription_meta', $post_id, $post );
|
||||
|
@@ -301,7 +301,9 @@ class WC_Subscription extends WC_Order {
|
||||
|
||||
if ( $new_status !== $old_status || ! in_array( $old_status_key, array_keys( wcs_get_subscription_statuses() ) ) ) {
|
||||
|
||||
// Only update is possible
|
||||
do_action( 'woocommerce_subscription_pre_update_status', $old_status, $new_status, $this );
|
||||
|
||||
// Only update if possible
|
||||
if ( ! $this->can_be_updated_to( $new_status ) ) {
|
||||
|
||||
$message = sprintf( __( 'Unable to change subscription status to "%s".', 'woocommerce-subscriptions' ), $new_status );
|
||||
|
@@ -101,8 +101,8 @@ class WC_Subscriptions_Cart {
|
||||
// Cache package rates. Hook in early to ensure we get a full set of rates.
|
||||
add_filter( 'woocommerce_package_rates', __CLASS__ . '::cache_package_rates', 1, 2 );
|
||||
|
||||
// When WooCommerce calculates rates for a recurring shipping package, only return the recurring shipping package rates
|
||||
add_filter( 'woocommerce_package_rates', __CLASS__ . '::filter_package_rates', 10, 2 );
|
||||
// When WooCommerce calculates rates for a recurring shipping package, make sure there is a different set of rates
|
||||
add_filter( 'woocommerce_shipping_packages', __CLASS__ . '::reset_shipping_method_counts', 1000, 1 );
|
||||
|
||||
// When WooCommerce determines the taxable address only return pick up shipping methods chosen for the recurring cart being calculated.
|
||||
add_filter( 'woocommerce_local_pickup_methods', __CLASS__ . '::filter_recurring_cart_chosen_shipping_method', 100 ,1 );
|
||||
@@ -434,6 +434,33 @@ class WC_Subscriptions_Cart {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When WooCommerce calculates rates for a recurring shipping package, we need to make sure there is a
|
||||
* different number of rates to make sure WooCommerce updates the chosen method for the recurring cart
|
||||
* and the 'woocommerce_shipping_chosen_method' filter is called, which we use to make sure the chosen
|
||||
* method is the recurring method, not the initial method.
|
||||
*
|
||||
* This function is hooked to 'woocommerce_shipping_packages' called by WC_Shipping->calculate_shipping()
|
||||
* which is why it accepts and returns the $packages array. It is also attached with a very high priority
|
||||
* to avoid conflicts with any 3rd party plugins that may use the method count session value (only a couple
|
||||
* of other hooks, including 'woocommerce_shipping_chosen_method' and 'woocommerce_shipping_method_chosen'
|
||||
* are triggered between when this callback runs on 'woocommerce_shipping_packages' and when the session
|
||||
* value is set again by WC_Shipping->calculate_shipping()).
|
||||
*
|
||||
* For more details, see: https://github.com/Prospress/woocommerce-subscriptions/pull/1187#issuecomment-186091152
|
||||
*
|
||||
* @param array $packages An array of shipping package of the form returned by WC_Cart->get_shipping_packages() which includes the package's contents, cost, customer, destination and alternative rates
|
||||
* @since 2.0.19
|
||||
*/
|
||||
public static function reset_shipping_method_counts( $packages ) {
|
||||
|
||||
if ( 'none' !== self::$recurring_cart_key ) {
|
||||
WC()->session->set( 'shipping_method_counts', array() );
|
||||
}
|
||||
|
||||
return $packages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the chosen shipping method for recurring cart calculations
|
||||
*
|
||||
@@ -456,32 +483,6 @@ class WC_Subscriptions_Cart {
|
||||
if ( 'none' !== self::$recurring_cart_key && isset( $chosen_methods[ $recurring_cart_package_key ] ) && isset( $available_methods[ $chosen_methods[ $recurring_cart_package_key ] ] ) ) {
|
||||
$default_method = $chosen_methods[ $recurring_cart_package_key ];
|
||||
|
||||
// Our dummy rate ended up being set as the default method (probably because it has no priority) so we need to re-run some logic from WC_Shipping::get_default_method() to find the actual default method
|
||||
} elseif ( 'wcs_dummy_rate' === $default_method && ! empty( $available_methods ) ) {
|
||||
|
||||
unset( $available_methods['wcs_dummy_rate'] );
|
||||
|
||||
// Order by priorities and costs
|
||||
$selection_priority = get_option( 'woocommerce_shipping_method_selection_priority', array() );
|
||||
$prioritized_methods = array();
|
||||
|
||||
foreach ( $available_methods as $method_key => $method ) {
|
||||
// Some IDs contain : if they have multiple rates so use $method->method_id
|
||||
$priority = isset( $selection_priority[ $method->method_id ] ) ? absint( $selection_priority[ $method->method_id ] ): 1;
|
||||
|
||||
if ( empty( $prioritized_methods[ $priority ] ) ) {
|
||||
$prioritized_methods[ $priority ] = array();
|
||||
}
|
||||
|
||||
$prioritized_methods[ $priority ][ $method_key ] = $method->cost;
|
||||
}
|
||||
|
||||
ksort( $prioritized_methods );
|
||||
$prioritized_methods = current( $prioritized_methods );
|
||||
asort( $prioritized_methods );
|
||||
|
||||
$default_method = current( array_keys( $prioritized_methods ) );
|
||||
|
||||
// Set the chosen shipping method (if available) to workaround WC_Shipping::get_default_method() setting the default shipping method whenever method count changes
|
||||
} elseif ( isset( $chosen_methods[ $package_index ] ) && $default_method !== $chosen_methods[ $package_index ] ) {
|
||||
$default_method = $chosen_methods[ $package_index ];
|
||||
@@ -516,58 +517,6 @@ class WC_Subscriptions_Cart {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When WooCommerce calculates rates for a recurring shipping package, we want to return both a different number
|
||||
* of rates, and a unique set of rates for the recurring shipping package to make sure WooCommerce updates the
|
||||
* chosen method for the recurring cart (and the 'woocommerce_shipping_chosen_method' filter is called, which
|
||||
* we use to make sure the chosen method is the recurring method, not the initial method).
|
||||
*
|
||||
* This function is hooked to 'woocommerce_package_rates' called by WC_Shipping->calculate_shipping_for_package()
|
||||
*
|
||||
* For more details, see:
|
||||
* - https://github.com/Prospress/woocommerce-subscriptions/pull/1187#issuecomment-186091152
|
||||
* - https://github.com/Prospress/woocommerce-subscriptions/pull/1187#issuecomment-187602311
|
||||
*
|
||||
* @param array $package_rates A set of shipping method objects in the form of WC_Shipping_Rate->id => WC_Shipping_Rate with the cost for that rate
|
||||
* @param array $package A shipping package of the form returned by WC_Cart->get_shipping_packages() which includes the package's contents, cost, customer, destination and alternative rates
|
||||
* @since 2.0.12
|
||||
*/
|
||||
public static function filter_package_rates( $package_rates, $package ) {
|
||||
|
||||
if ( 'none' !== self::$recurring_cart_key ) {
|
||||
|
||||
$chosen_methods = WC()->session->get( 'chosen_shipping_methods', array() );
|
||||
$recurring_cart_shipping_methods = array();
|
||||
$recurring_package_count = 0;
|
||||
|
||||
foreach ( $chosen_methods as $package_index => $chosen_method_name ) {
|
||||
if ( self::get_recurring_shipping_package_key( self::$recurring_cart_key, $recurring_package_count ) == $package_index ) {
|
||||
$recurring_cart_shipping_methods[ $chosen_method_name ] = $chosen_method_name;
|
||||
$recurring_package_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( 0 < count( $recurring_cart_shipping_methods ) ) {
|
||||
|
||||
$unique_package_rates = array_intersect_key( $package_rates, $recurring_cart_shipping_methods );
|
||||
|
||||
// if we have no unique package rates, the cached chosen shipping method has been disabled or is no longer available, so instead of filtering the available rates to only that method, we need to add a new dummy method to make sure the available rates count is different, this is only necessary when there is only one available method because when there is more than one, the selection fields will be displayed and the customer can choose the method
|
||||
if ( empty( $unique_package_rates ) && 1 == count( $package_rates ) ) {
|
||||
$package_rates['wcs_dummy_rate'] = current( $package_rates );
|
||||
} else {
|
||||
$package_rates = $unique_package_rates;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to make sure both the number of rates and the contents of each rate are different to ensure that we bypass WC's cache, so let's add our own unique key on the rate
|
||||
foreach ( $package_rates as $method_name => $method ) {
|
||||
$package_rates[ $method_name ]->recurring_cart_key = self::$recurring_cart_key;
|
||||
}
|
||||
}
|
||||
|
||||
return $package_rates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether all the subscription product items in the cart have a free trial.
|
||||
*
|
||||
@@ -1101,6 +1050,10 @@ class WC_Subscriptions_Cart {
|
||||
$added_invalid_notice = false;
|
||||
$standard_packages = WC()->shipping->get_packages();
|
||||
|
||||
// temporarily store the current calculation type so we can restore it later
|
||||
$calculation_type = self::$calculation_type;
|
||||
self::$calculation_type = 'recurring_total';
|
||||
|
||||
foreach ( WC()->cart->recurring_carts as $recurring_cart_key => $recurring_cart ) {
|
||||
|
||||
if ( false === $recurring_cart->needs_shipping() || 0 == $recurring_cart->next_payment_date ) {
|
||||
@@ -1131,6 +1084,8 @@ class WC_Subscriptions_Cart {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$calculation_type = $calculation_type;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2206,5 +2161,27 @@ class WC_Subscriptions_Cart {
|
||||
WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_method_cache );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When WooCommerce calculates rates for a recurring shipping package, previously we would return both a different number
|
||||
* of rates, and a unique set of rates for the recurring shipping package to make sure WooCommerce updated the
|
||||
* chosen method for the recurring cart (and the 'woocommerce_shipping_chosen_method' filter was called, which
|
||||
* we use to make sure the chosen method is the recurring method, not the initial method).
|
||||
*
|
||||
* This is no longer necessary with the introductino of self::reset_shipping_method_counts() which achieves the same thing
|
||||
* via a different means, while allowing WooCommerce's cached rates to be used and avoiding the issue reported in
|
||||
* https://github.com/Prospress/woocommerce-subscriptions/issues/1583
|
||||
*
|
||||
* This function is hooked to 'woocommerce_package_rates' called by WC_Shipping->calculate_shipping_for_package()
|
||||
*
|
||||
* @param array $package_rates A set of shipping method objects in the form of WC_Shipping_Rate->id => WC_Shipping_Rate with the cost for that rate
|
||||
* @param array $package A shipping package of the form returned by WC_Cart->get_shipping_packages() which includes the package's contents, cost, customer, destination and alternative rates
|
||||
* @since 2.0.12
|
||||
*/
|
||||
public static function filter_package_rates( $package_rates, $package ) {
|
||||
_deprecated_function( __METHOD__, '2.0.19' );
|
||||
return $package_rates;
|
||||
}
|
||||
}
|
||||
WC_Subscriptions_Cart::init();
|
||||
|
@@ -50,8 +50,8 @@ class WC_Subscriptions_Order {
|
||||
// Add dropdown to admin orders screen to filter on order type
|
||||
add_action( 'restrict_manage_posts', __CLASS__ . '::restrict_manage_subscriptions', 50 );
|
||||
|
||||
// Add filer to queries on admin orders screen to filter on order type
|
||||
add_filter( 'request', __CLASS__ . '::orders_by_type_query' );
|
||||
// Add filter to queries on admin orders screen to filter on order type. To avoid WC overriding our query args, we need to hook on after them on 10.
|
||||
add_filter( 'request', __CLASS__ . '::orders_by_type_query', 11 );
|
||||
|
||||
// Don't display migrated order item meta on the Edit Order screen
|
||||
add_filter( 'woocommerce_hidden_order_itemmeta', __CLASS__ . '::hide_order_itemmeta' );
|
||||
@@ -673,26 +673,16 @@ class WC_Subscriptions_Order {
|
||||
if ( 'shop_order' == $typenow && isset( $_GET['shop_order_subtype'] ) ) {
|
||||
|
||||
if ( 'Original' == $_GET['shop_order_subtype'] ) {
|
||||
$key = 'post__not_in';
|
||||
$compare_operator = 'NOT EXISTS';
|
||||
} elseif ( 'Renewal' == $_GET['shop_order_subtype'] ) {
|
||||
$key = 'post__in';
|
||||
$compare_operator = 'EXISTS';
|
||||
}
|
||||
|
||||
if ( ! empty( $key ) ) {
|
||||
$vars[ $key ] = get_posts( array(
|
||||
'posts_per_page' => -1,
|
||||
'post_type' => 'shop_order',
|
||||
'post_status' => 'any',
|
||||
'fields' => 'ids',
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
if ( ! empty( $compare_operator ) ) {
|
||||
$vars['meta_query'][] = array(
|
||||
'key' => '_subscription_renewal',
|
||||
'compare' => 'EXISTS',
|
||||
),
|
||||
),
|
||||
) );
|
||||
'compare' => $compare_operator,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -66,6 +66,12 @@ class WC_Subscriptions_Product {
|
||||
|
||||
// Handle bulk edits to subscription data in WC 2.4
|
||||
add_action( 'woocommerce_bulk_edit_variations', __CLASS__ . '::bulk_edit_variations', 10, 4 );
|
||||
|
||||
// check product variations for sync'd or trial
|
||||
add_action( 'wp_ajax_wcs_product_has_trial_or_is_synced', __CLASS__ . '::check_product_variations_for_syncd_or_trial' );
|
||||
|
||||
// maybe update the One Time Shipping product setting when users edit variations using bulk actions and the variation level save
|
||||
add_action( 'wp_ajax_wcs_update_one_time_shipping', __CLASS__ . '::maybe_update_one_time_shipping_on_variation_edits' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -933,7 +939,9 @@ class WC_Subscriptions_Product {
|
||||
*/
|
||||
public static function bulk_edit_variations( $bulk_action, $data, $variable_product_id, $variation_ids ) {
|
||||
|
||||
if ( WC_Subscriptions::is_woocommerce_pre( '2.5' ) ) {
|
||||
if ( ! isset( $data['value'] ) ) {
|
||||
return;
|
||||
} elseif ( WC_Subscriptions::is_woocommerce_pre( '2.5' ) ) {
|
||||
// Pre 2.5 we don't have the product type information available so we have to check if it is a subscription - downside here is this only works if the product has already been saved
|
||||
if ( ! self::is_subscription( $variable_product_id ) ) {
|
||||
return;
|
||||
@@ -995,11 +1003,11 @@ class WC_Subscriptions_Product {
|
||||
|
||||
if ( is_object( $order ) && $order->has_status( array( 'pending', 'failed' ) ) ) {
|
||||
foreach ( $order->get_items() as $item ) {
|
||||
if ( $item['product_id'] == $product->id || $item['variation_id'] == $product->id ) {
|
||||
if ( $item['product_id'] == $product_id || $item['variation_id'] == $product_id ) {
|
||||
|
||||
$subscriptions = wcs_get_subscriptions( array(
|
||||
'order_id' => $order->id,
|
||||
'product_id' => $product->id,
|
||||
'product_id' => $product_id,
|
||||
) );
|
||||
|
||||
if ( ! empty( $subscriptions ) ) {
|
||||
@@ -1019,6 +1027,70 @@ class WC_Subscriptions_Product {
|
||||
return self::$order_awaiting_payment_for_product[ $product_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an AJAX request to check if a product has a variation which is either sync'd or has a trial.
|
||||
* Once at least one variation with a trial or sync date is found, this will terminate and return true, otherwise false.
|
||||
*
|
||||
* @since 2.0.18
|
||||
*/
|
||||
public static function check_product_variations_for_syncd_or_trial() {
|
||||
|
||||
check_admin_referer( 'one_time_shipping', 'nonce' );
|
||||
|
||||
$product = wc_get_product( $_POST['product_id'] );
|
||||
$is_synced_or_has_trial = false;
|
||||
|
||||
if ( WC_Subscriptions_Product::is_subscription( $product ) ) {
|
||||
|
||||
foreach ( $product->get_children() as $variation_id ) {
|
||||
|
||||
if ( isset( $_POST['variations_checked'] ) && in_array( $variation_id, $_POST['variations_checked'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$variable_product = wc_get_product( $variation_id );
|
||||
|
||||
if ( $variable_product->subscription_trial_length > 0 ) {
|
||||
$is_synced_or_has_trial = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( WC_Subscriptions_Synchroniser::is_syncing_enabled() && ( ( ! is_array( $variable_product->subscription_payment_sync_date ) && $variable_product->subscription_payment_sync_date > 0 ) || ( is_array( $variable_product->subscription_payment_sync_date ) && $variable_product->subscription_payment_sync_date['day'] > 0 ) ) ) {
|
||||
$is_synced_or_has_trial = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json( array( 'is_synced_or_has_trial' => $is_synced_or_has_trial ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an AJAX request to update a product's One Time Shipping setting after a bulk variation edit has been made.
|
||||
* After bulk edits (variation level saving as well as variation bulk actions), variation data has been updated in the
|
||||
* database and therefore doesn't require the product global settings to be updated by the user for the changes to take effect.
|
||||
* This function, triggered after saving variations or triggering the trial length bulk action, ensures one time shipping settings
|
||||
* are updated after determining if one time shipping is still available to the product.
|
||||
*
|
||||
* @since 2.0.18
|
||||
*/
|
||||
public static function maybe_update_one_time_shipping_on_variation_edits() {
|
||||
|
||||
check_admin_referer( 'one_time_shipping', 'nonce' );
|
||||
|
||||
$one_time_shipping_enabled = $_POST['one_time_shipping_enabled'];
|
||||
$one_time_shipping_selected = $_POST['one_time_shipping_selected'];
|
||||
$subscription_one_time_shipping = 'no';
|
||||
|
||||
if ( 'false' !== $one_time_shipping_enabled && 'true' === $one_time_shipping_selected ) {
|
||||
$subscription_one_time_shipping = 'yes';
|
||||
}
|
||||
|
||||
update_post_meta( $_POST['product_id'], '_subscription_one_time_shipping', $subscription_one_time_shipping );
|
||||
|
||||
wp_send_json( array( 'one_time_shipping' => $subscription_one_time_shipping ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a price (could be per period price or sign-up fee) for a subscription less tax
|
||||
* if the subscription is taxable and the prices in the store include tax.
|
||||
|
@@ -167,8 +167,9 @@ class WC_Subscriptions_Switcher {
|
||||
foreach ( $switch_items as $cart_item_key => $switch_item ) {
|
||||
|
||||
$subscription = wcs_get_subscription( $switch_item['subscription_id'] );
|
||||
$line_item = wcs_get_order_item( $switch_item['item_id'], $subscription );
|
||||
|
||||
if ( ! is_object( $subscription ) || ! current_user_can( 'switch_shop_subscription', $subscription->id ) || ! wcs_is_product_switchable_type( WC()->cart->cart_contents[ $cart_item_key ]['data'] ) ) {
|
||||
if ( ! is_object( $subscription ) || empty( $line_item ) || ! self::can_item_be_switched_by_user( $line_item, $subscription ) ) {
|
||||
WC()->cart->remove_cart_item( $cart_item_key );
|
||||
$removed_item_count++;
|
||||
}
|
||||
@@ -214,7 +215,7 @@ class WC_Subscriptions_Switcher {
|
||||
}
|
||||
|
||||
// If the product is limited
|
||||
if ( 'any' == $limitation || $subscription->has_status( $limitation ) || ( 'active' == $limitation && $subscription->has_status( 'on-hold' ) ) ) {
|
||||
if ( 'any' == $limitation || $subscription->has_status( $limitation ) ) {
|
||||
|
||||
$subscribed_notice = __( 'You have already subscribed to this product and it is limited to one per customer. You can not purchase the product again.', 'woocommerce-subscriptions' );
|
||||
|
||||
@@ -247,9 +248,11 @@ class WC_Subscriptions_Switcher {
|
||||
}
|
||||
}
|
||||
|
||||
if ( self::can_item_be_switched_by_user( $item, $subscription ) ) {
|
||||
wp_redirect( add_query_arg( 'auto-switch', 'true', self::get_switch_url( $item_id, $item, $subscription ) ) );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
WC_Subscriptions::add_notice( $subscribed_notice, 'notice' );
|
||||
|
@@ -50,18 +50,16 @@ class WCS_Cache_Manager_TLC extends WCS_Cache_Manager {
|
||||
* @param string $key The key to cache the data with
|
||||
* @param string|array $callback name of function, or array of class - method that fetches the data
|
||||
* @param array $params arguments passed to $callback
|
||||
* @param integer $expires number of seconds for how long to keep the cache. Don't set it to 0, as the cache will be autoloaded. Default is a week.
|
||||
*
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function cache_and_get( $key, $callback, $params = array(), $expires = 0 ) {
|
||||
public function cache_and_get( $key, $callback, $params = array(), $expires = WEEK_IN_SECONDS ) {
|
||||
$expires = absint( $expires );
|
||||
|
||||
$transient = tlc_transient( $key )
|
||||
->updates_with( $callback, $params );
|
||||
|
||||
if ( $expires ) {
|
||||
$transient->expires_in( $expires );
|
||||
}
|
||||
->updates_with( $callback, $params )
|
||||
->expires_in( $expires );
|
||||
|
||||
return $transient->get();
|
||||
}
|
||||
|
@@ -62,7 +62,7 @@ class WCS_Query extends WC_Query {
|
||||
*/
|
||||
public function change_endpoint_title( $title ) {
|
||||
|
||||
if ( in_the_loop() ) {
|
||||
if ( in_the_loop() && is_account_page() ) {
|
||||
foreach ( $this->query_vars as $key => $query_var ) {
|
||||
if ( $this->is_query( $query_var ) ) {
|
||||
$title = $this->get_endpoint_title( $key );
|
||||
|
@@ -32,18 +32,6 @@ abstract class ActionScheduler {
|
||||
return ActionScheduler_AdminView::instance();
|
||||
}
|
||||
|
||||
public static function get_datetime_object( $when ) {
|
||||
$when = empty($when) ? time() : $when;
|
||||
if ( is_object($when) && $when instanceof DateTime ) {
|
||||
$date = $when;
|
||||
} elseif ( is_numeric( $when ) ) {
|
||||
$date = new DateTime( '@'.$when );
|
||||
} else {
|
||||
$date = new DateTime( $when );
|
||||
}
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the absolute system path to the plugin directory, or a file therein
|
||||
* @static
|
||||
@@ -121,5 +109,12 @@ abstract class ActionScheduler {
|
||||
}
|
||||
|
||||
final private function __construct() {}
|
||||
|
||||
/** Deprecated **/
|
||||
|
||||
public static function get_datetime_object( $when = null, $timezone = 'UTC' ) {
|
||||
_deprecated_function( __METHOD__, '2.0', 'wcs_add_months()' );
|
||||
return as_get_datetime_object( $when, $timezone );
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ class ActionScheduler_ActionFactory {
|
||||
* @return string The ID of the stored action
|
||||
*/
|
||||
public function single( $hook, $args = array(), $when = NULL, $group = '' ) {
|
||||
$date = ActionScheduler::get_datetime_object( $when );
|
||||
$date = as_get_datetime_object( $when );
|
||||
$schedule = new ActionScheduler_SimpleSchedule( $date );
|
||||
$action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
|
||||
return $this->store( $action );
|
||||
@@ -32,7 +32,7 @@ class ActionScheduler_ActionFactory {
|
||||
if ( empty($interval) ) {
|
||||
return $this->single( $hook, $args, $first, $group );
|
||||
}
|
||||
$date = ActionScheduler::get_datetime_object( $first );
|
||||
$date = as_get_datetime_object( $first );
|
||||
$schedule = new ActionScheduler_IntervalSchedule( $date, $interval );
|
||||
$action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
|
||||
return $this->store( $action );
|
||||
@@ -52,7 +52,7 @@ class ActionScheduler_ActionFactory {
|
||||
if ( empty($schedule) ) {
|
||||
return $this->single( $hook, $args, $first, $group );
|
||||
}
|
||||
$date = ActionScheduler::get_datetime_object( $first );
|
||||
$date = as_get_datetime_object( $first );
|
||||
$cron = CronExpression::factory( $schedule );
|
||||
$schedule = new ActionScheduler_CronSchedule( $date, $cron );
|
||||
$action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
|
||||
|
@@ -166,44 +166,12 @@ class ActionScheduler_AdminView {
|
||||
|
||||
$action_title = ( 'trash' == $post->post_status ) ? $post->post_title : $action->get_hook();
|
||||
$recurrence = ( 'trash' == $post->post_status ) ? 0 : $action->get_schedule();
|
||||
$next_timestamp = get_post_time( 'U', true, $post_id );
|
||||
$next_timestamp = as_get_datetime_object( $post->post_date_gmt )->format( 'U' );
|
||||
$status = get_post_status( $post_id );
|
||||
|
||||
switch ( $column_name ) {
|
||||
case 'hook':
|
||||
|
||||
echo $action_title;
|
||||
|
||||
$actions = array();
|
||||
|
||||
if ( current_user_can( 'edit_post', $post->ID ) && ! in_array( $post->post_status, array( 'publish', 'in-progress', 'trash' ) ) ) {
|
||||
$actions['process'] = "<a title='" . esc_attr( __( 'Process the action now as if it were run as part of a queue' ) ) . "' href='" . self::get_run_action_link( $post->ID, 'process' ) . "'>" . __( 'Run', 'action-scheduler' ) . "</a>";
|
||||
}
|
||||
|
||||
if ( current_user_can( 'delete_post', $post->ID ) ) {
|
||||
if ( 'trash' == $post->post_status ) {
|
||||
$post_type_object = get_post_type_object( $post->post_type );
|
||||
$actions['untrash'] = "<a title='" . esc_attr( __( 'Restore this action from the Trash' ) ) . "' href='" . wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ) . "'>" . __( 'Restore', 'action-scheduler' ) . "</a>";
|
||||
} elseif ( EMPTY_TRASH_DAYS ) {
|
||||
$actions['trash'] = "<a class='submitdelete' title='" . esc_attr( __( 'Move this action to the Trash' ) ) . "' href='" . get_delete_post_link( $post->ID ) . "'>" . __( 'Trash', 'action-scheduler' ) . "</a>";
|
||||
}
|
||||
|
||||
if ( 'trash' == $post->post_status || !EMPTY_TRASH_DAYS ) {
|
||||
$actions['delete'] = "<a class='submitdelete' title='" . esc_attr( __( 'Delete this action permanently' ) ) . "' href='" . get_delete_post_link( $post->ID, '', true ) . "'>" . __( 'Delete Permanently', 'action-scheduler' ) . "</a>";
|
||||
}
|
||||
}
|
||||
|
||||
$action_count = count( $actions );
|
||||
$i = 0;
|
||||
|
||||
echo '<div class="row-actions">';
|
||||
foreach ( $actions as $a => $link ) {
|
||||
++$i;
|
||||
( $i == $action_count ) ? $sep = '' : $sep = ' | ';
|
||||
echo "<span class='$a'>$link$sep</span>";
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
break;
|
||||
case 'status':
|
||||
if ( 'publish' == $status ) {
|
||||
@@ -228,7 +196,7 @@ class ActionScheduler_AdminView {
|
||||
}
|
||||
break;
|
||||
case 'scheduled':
|
||||
echo get_date_from_gmt( date( 'Y-m-d H:i:s', $next_timestamp ), 'Y-m-d H:i:s' );
|
||||
echo get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $next_timestamp ), 'Y-m-d H:i:s' );
|
||||
if ( gmdate( 'U' ) > $next_timestamp ) {
|
||||
printf( __( ' (%s ago)', 'action-scheduler' ), human_time_diff( gmdate( 'U' ), $next_timestamp ) );
|
||||
} else {
|
||||
@@ -260,10 +228,23 @@ class ActionScheduler_AdminView {
|
||||
*/
|
||||
public static function row_actions( $actions, $post ) {
|
||||
|
||||
if ( ActionScheduler_wpPostStore::POST_TYPE == $post->post_type && isset( $actions['edit'] ) ) {
|
||||
if ( ActionScheduler_wpPostStore::POST_TYPE == $post->post_type ) {
|
||||
|
||||
if ( isset( $actions['edit'] ) ) {
|
||||
unset( $actions['edit'] );
|
||||
}
|
||||
|
||||
if ( isset( $actions['inline hide-if-no-js'] ) ) {
|
||||
unset( $actions['inline hide-if-no-js'] );
|
||||
}
|
||||
|
||||
if ( current_user_can( 'edit_post', $post->ID ) && ! in_array( $post->post_status, array( 'publish', 'in-progress', 'trash' ) ) ) {
|
||||
$actions['process'] = "<a title='" . esc_attr( __( 'Process the action now as if it were run as part of a queue' ) ) . "' href='" . self::get_run_action_link( $post->ID, 'process' ) . "'>" . __( 'Run', 'action-scheduler' ) . "</a>";
|
||||
}
|
||||
|
||||
ksort( $actions );
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
|
@@ -20,7 +20,7 @@ class ActionScheduler_CronSchedule implements ActionScheduler_Schedule {
|
||||
* @return DateTime|null
|
||||
*/
|
||||
public function next( DateTime $after = NULL ) {
|
||||
$after = empty($after) ? clone($this->start) : clone($after);
|
||||
$after = empty($after) ? clone $this->start : clone $after;
|
||||
return $this->cron->getNextRunDate($after, 0, TRUE);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class ActionScheduler_CronSchedule implements ActionScheduler_Schedule {
|
||||
}
|
||||
|
||||
public function __wakeup() {
|
||||
$this->start = new DateTime('@'.$this->start_timestamp);
|
||||
$this->start = as_get_datetime_object($this->start_timestamp);
|
||||
}
|
||||
}
|
||||
|
@@ -20,12 +20,12 @@ class ActionScheduler_IntervalSchedule implements ActionScheduler_Schedule {
|
||||
* @return DateTime|null
|
||||
*/
|
||||
public function next( DateTime $after = NULL ) {
|
||||
$after = empty($after) ? new DateTime('@0') : clone($after);
|
||||
$after = empty($after) ? as_get_datetime_object('@0') : clone $after;
|
||||
if ( $after > $this->start ) {
|
||||
$after->modify('+'.$this->interval_in_seconds.' seconds');
|
||||
return $after;
|
||||
}
|
||||
return clone( $this->start );
|
||||
return clone $this->start;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +50,7 @@ class ActionScheduler_IntervalSchedule implements ActionScheduler_Schedule {
|
||||
}
|
||||
|
||||
public function __wakeup() {
|
||||
$this->start = new DateTime('@'.$this->start_timestamp);
|
||||
$this->start = as_get_datetime_object($this->start_timestamp);
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ class ActionScheduler_QueueCleaner {
|
||||
|
||||
public function delete_old_actions() {
|
||||
$lifespan = apply_filters( 'action_scheduler_retention_period', $this->month_in_seconds );
|
||||
$cutoff = new DateTime($lifespan.' seconds ago');
|
||||
$cutoff = as_get_datetime_object($lifespan.' seconds ago');
|
||||
|
||||
$actions_to_delete = $this->store->query_actions( array(
|
||||
'status' => ActionScheduler_Store::STATUS_COMPLETE,
|
||||
@@ -35,7 +35,7 @@ class ActionScheduler_QueueCleaner {
|
||||
if ( $timeout < 0 ) {
|
||||
return;
|
||||
}
|
||||
$cutoff = new DateTime($timeout.' seconds ago');
|
||||
$cutoff = as_get_datetime_object($timeout.' seconds ago');
|
||||
$actions_to_reset = $this->store->query_actions( array(
|
||||
'status' => ActionScheduler_Store::STATUS_PENDING,
|
||||
'modified' => $cutoff,
|
||||
@@ -55,7 +55,7 @@ class ActionScheduler_QueueCleaner {
|
||||
if ( $timeout < 0 ) {
|
||||
return;
|
||||
}
|
||||
$cutoff = new DateTime($timeout.' seconds ago');
|
||||
$cutoff = as_get_datetime_object($timeout.' seconds ago');
|
||||
$actions_to_reset = $this->store->query_actions( array(
|
||||
'status' => ActionScheduler_Store::STATUS_RUNNING,
|
||||
'modified' => $cutoff,
|
||||
|
@@ -56,7 +56,7 @@ class ActionScheduler_QueueRunner {
|
||||
if ( $this->store->get_claim_count() < apply_filters( 'action_scheduler_queue_runner_concurrent_batches', 5 ) ) {
|
||||
$batch_size = apply_filters( 'action_scheduler_queue_runner_batch_size', 25 );
|
||||
$this->monitor = new ActionScheduler_FatalErrorMonitor( $this->store );
|
||||
$actions_run = $this->do_batch( $batch_size );
|
||||
$count = $this->do_batch( $batch_size );
|
||||
unset( $this->monitor );
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ class ActionScheduler_QueueRunner {
|
||||
}
|
||||
|
||||
protected function schedule_next_instance( ActionScheduler_Action $action ) {
|
||||
$next = $action->get_schedule()->next( new DateTime() );
|
||||
$next = $action->get_schedule()->next( as_get_datetime_object() );
|
||||
if ( $next ) {
|
||||
$this->store->save_action( $action, $next );
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ class ActionScheduler_SimpleSchedule implements ActionScheduler_Schedule {
|
||||
private $date = NULL;
|
||||
private $timestamp = 0;
|
||||
public function __construct( DateTime $date ) {
|
||||
$this->date = clone($date);
|
||||
$this->date = clone $date;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -16,8 +16,8 @@ class ActionScheduler_SimpleSchedule implements ActionScheduler_Schedule {
|
||||
* @return DateTime|null
|
||||
*/
|
||||
public function next( DateTime $after = NULL ) {
|
||||
$after = empty($after) ? new DateTime('@0') : $after;
|
||||
return ( $after > $this->date ) ? NULL : clone( $this->date );
|
||||
$after = empty($after) ? as_get_datetime_object('@0') : $after;
|
||||
return ( $after > $this->date ) ? NULL : clone $this->date;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,7 +32,7 @@ class ActionScheduler_SimpleSchedule implements ActionScheduler_Schedule {
|
||||
}
|
||||
|
||||
public function __wakeup() {
|
||||
$this->date = new DateTime('@'.$this->timestamp);
|
||||
$this->date = as_get_datetime_object($this->timestamp);
|
||||
}
|
||||
}
|
||||
|
@@ -16,19 +16,21 @@ class ActionScheduler_wpCommentLogger extends ActionScheduler_Logger {
|
||||
*/
|
||||
public function log( $action_id, $message, DateTime $date = NULL ) {
|
||||
if ( empty($date) ) {
|
||||
$date = new DateTime();
|
||||
$date = as_get_datetime_object();
|
||||
} else {
|
||||
$date = clone( $date );
|
||||
$date = clone $date;
|
||||
}
|
||||
$comment_id = $this->create_wp_comment( $action_id, $message, $date );
|
||||
return $comment_id;
|
||||
}
|
||||
|
||||
protected function create_wp_comment( $action_id, $message, DateTime $date ) {
|
||||
$comment_date_gmt = $date->format('Y-m-d H:i:s');
|
||||
$date->setTimezone( ActionScheduler_TimezoneHelper::get_local_timezone() );
|
||||
$comment_data = array(
|
||||
'comment_post_ID' => $action_id,
|
||||
'comment_date' => $date->format('Y-m-d H:i:s'),
|
||||
'comment_date_gmt' => $comment_date_gmt,
|
||||
'comment_author' => self::AGENT,
|
||||
'comment_content' => $message,
|
||||
'comment_agent' => self::AGENT,
|
||||
|
@@ -176,7 +176,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
$order = 'ASC';
|
||||
break;
|
||||
}
|
||||
$query .= " ORDER BY post_date $order LIMIT 1";
|
||||
$query .= " ORDER BY post_date_gmt $order LIMIT 1";
|
||||
|
||||
$query = $wpdb->prepare( $query, $args );
|
||||
|
||||
@@ -239,20 +239,20 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
}
|
||||
|
||||
if ( $query['date'] instanceof DateTime ) {
|
||||
$date = clone( $query['date'] );
|
||||
$date->setTimezone( $this->get_local_timezone() );
|
||||
$date = clone $query['date'];
|
||||
$date->setTimezone( new DateTimeZone('UTC') );
|
||||
$date_string = $date->format('Y-m-d H:i:s');
|
||||
$comparator = $this->validate_sql_comparator($query['date_compare']);
|
||||
$sql .= " AND p.post_date $comparator %s";
|
||||
$sql .= " AND p.post_date_gmt $comparator %s";
|
||||
$sql_params[] = $date_string;
|
||||
}
|
||||
|
||||
if ( $query['modified'] instanceof DateTime ) {
|
||||
$modified = clone( $query['modified'] );
|
||||
$modified->setTimezone( $this->get_local_timezone() );
|
||||
$modified = clone $query['modified'];
|
||||
$modified->setTimezone( new DateTimeZone('UTC') );
|
||||
$date_string = $modified->format('Y-m-d H:i:s');
|
||||
$comparator = $this->validate_sql_comparator($query['modified_compare']);
|
||||
$sql .= " AND p.post_modified $comparator %s";
|
||||
$sql .= " AND p.post_modified_gmt $comparator %s";
|
||||
$sql_params[] = $date_string;
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
break;
|
||||
case 'date':
|
||||
default:
|
||||
$orderby = 'p.post_date';
|
||||
$orderby = 'p.post_date_gmt';
|
||||
break;
|
||||
}
|
||||
if ( strtoupper($query['order']) == 'ASC' ) {
|
||||
@@ -336,18 +336,28 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
* @return DateTime The date the action is schedule to run, or the date that it ran.
|
||||
*/
|
||||
public function get_date( $action_id ) {
|
||||
$date = $this->get_date_gmt( $action_id );
|
||||
return $date->setTimezone( $this->get_local_timezone() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action_id
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @return DateTime The date the action is schedule to run, or the date that it ran.
|
||||
*/
|
||||
public function get_date_gmt( $action_id ) {
|
||||
$post = get_post($action_id);
|
||||
if ( empty($post) || ($post->post_type != self::POST_TYPE) ) {
|
||||
throw new InvalidArgumentException(sprintf(__('Unidentified action %s', 'action-scheduler'), $action_id));
|
||||
}
|
||||
if ( $post->post_status == 'publish' ) {
|
||||
return new DateTime($post->post_modified, ActionScheduler_TimezoneHelper::get_local_timezone());
|
||||
return as_get_datetime_object($post->post_modified_gmt);
|
||||
} else {
|
||||
return new DateTime($post->post_date, ActionScheduler_TimezoneHelper::get_local_timezone());
|
||||
return as_get_datetime_object($post->post_date_gmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $max_actions
|
||||
* @param DateTime $before_date Jobs must be schedule before this date. Defaults to now.
|
||||
@@ -381,17 +391,17 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
/**
|
||||
* @param string $claim_id
|
||||
* @param int $limit
|
||||
* @param DateTime $before_date
|
||||
* @param DateTime $before_date Should use UTC timezone.
|
||||
* @return int The number of actions that were claimed
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function claim_actions( $claim_id, $limit, DateTime $before_date = NULL ) {
|
||||
/** @var wpdb $wpdb */
|
||||
global $wpdb;
|
||||
$date = is_null($before_date) ? new DateTime() : clone( $before_date );
|
||||
$date->setTimezone( $this->get_local_timezone() ); // using post_modified to take advantage of indexes
|
||||
// can't use $wpdb->update() because of the <= condition
|
||||
$sql = "UPDATE {$wpdb->posts} SET post_password = %s, post_modified_gmt = %s, post_modified = %s WHERE post_type = %s AND post_status = %s AND post_password = '' AND post_date <= %s ORDER BY menu_order ASC, post_date ASC LIMIT %d";
|
||||
|
||||
$date = is_null($before_date) ? as_get_datetime_object() : clone $before_date;
|
||||
// can't use $wpdb->update() because of the <= condition, using post_modified to take advantage of indexes
|
||||
$sql = "UPDATE {$wpdb->posts} SET post_password = %s, post_modified_gmt = %s, post_modified = %s WHERE post_type = %s AND post_status = %s AND post_password = '' AND post_date_gmt <= %s ORDER BY menu_order ASC, post_date_gmt ASC LIMIT %d";
|
||||
$sql = $wpdb->prepare( $sql, array( $claim_id, current_time('mysql', true), current_time('mysql'), self::POST_TYPE, 'pending', $date->format('Y-m-d H:i:s'), $limit ) );
|
||||
$rows_affected = $wpdb->query($sql);
|
||||
if ( $rows_affected === false ) {
|
||||
|
@@ -116,9 +116,9 @@ function wc_next_scheduled_action( $hook, $args = NULL, $group = '' ) {
|
||||
* @param array $args Possible arguments, with their default values:
|
||||
* 'hook' => '' - the name of the action that will be triggered
|
||||
* 'args' => NULL - the args array that will be passed with the action
|
||||
* 'date' => NULL - the scheduled date of the action. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime().
|
||||
* 'date' => NULL - the scheduled date of the action. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone.
|
||||
* 'date_compare' => '<=' - operator for testing "date". accepted values are '!=', '>', '>=', '<', '<=', '='
|
||||
* 'modified' => NULL - the date the action was last updated. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime().
|
||||
* 'modified' => NULL - the date the action was last updated. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone.
|
||||
* 'modified_compare' => '<=' - operator for testing "modified". accepted values are '!=', '>', '>=', '<', '<=', '='
|
||||
* 'group' => '' - the group the action belongs to
|
||||
* 'status' => '' - ActionScheduler_Store::STATUS_COMPLETE or ActionScheduler_Store::STATUS_PENDING
|
||||
@@ -136,7 +136,7 @@ function wc_get_scheduled_actions( $args = array(), $return_format = OBJECT ) {
|
||||
$store = ActionScheduler::store();
|
||||
foreach ( array('date', 'modified') as $key ) {
|
||||
if ( isset($args[$key]) ) {
|
||||
$args[$key] = ActionScheduler::get_datetime_object($args[$key]);
|
||||
$args[$key] = as_get_datetime_object($args[$key]);
|
||||
}
|
||||
}
|
||||
$ids = $store->query_actions( $args );
|
||||
@@ -158,3 +158,31 @@ function wc_get_scheduled_actions( $args = array(), $return_format = OBJECT ) {
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create an instance of DateTime based on a given
|
||||
* string and timezone. By default, will return the current date/time
|
||||
* in the UTC timezone.
|
||||
*
|
||||
* Needed because new DateTime() called without an explicit timezone
|
||||
* will create a date/time in PHP's timezone, but we need to have
|
||||
* assurance that a date/time uses the right timezone (which we almost
|
||||
* always want to be UTC), which means we need to always include the
|
||||
* timezone when instantiating datetimes rather than leaving it up to
|
||||
* the PHP default.
|
||||
*
|
||||
* @param mixed $date_string A date/time string. Valid formats are explained in http://php.net/manual/en/datetime.formats.php
|
||||
* @param string $timezone A timezone identifier, like UTC or Europe/Lisbon. The list of valid identifiers is available http://php.net/manual/en/timezones.php
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
function as_get_datetime_object( $date_string = null, $timezone = 'UTC' ) {
|
||||
if ( is_object($date_string) && $date_string instanceof DateTime ) {
|
||||
$date = $date_string->setTimezone(new DateTimeZone( $timezone ) );
|
||||
} elseif ( is_numeric( $date_string ) ) {
|
||||
$date = new DateTime( '@'.$date_string, new DateTimeZone( $timezone ) );
|
||||
} else {
|
||||
$date = new DateTime( $date_string, new DateTimeZone( $timezone ) );
|
||||
}
|
||||
return $date;
|
||||
}
|
||||
|
@@ -65,7 +65,7 @@ function wcs_cart_totals_shipping_html() {
|
||||
$chosen_initial_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
|
||||
$chosen_recurring_method = isset( WC()->session->chosen_shipping_methods[ $recurring_cart_key . '_' . $i ] ) ? WC()->session->chosen_shipping_methods[ $recurring_cart_key . '_' . $i ] : $chosen_initial_method;
|
||||
|
||||
if ( ( 1 === count( $package['rates'] ) ) || ( isset( $initial_packages[ $i ] ) && $package['rates'] == $initial_packages[ $i ]['rates'] && apply_filters( 'wcs_cart_totals_shipping_html_price_only', true, $package, $recurring_cart ) ) ) {
|
||||
if ( ( 1 === count( $package['rates'] ) ) || ( isset( $package['rates'][ $chosen_initial_method ] ) && isset( $initial_packages[ $i ] ) && $package['rates'] == $initial_packages[ $i ]['rates'] && apply_filters( 'wcs_cart_totals_shipping_html_price_only', true, $package, $recurring_cart ) ) ) {
|
||||
$shipping_method = ( 1 === count( $package['rates'] ) ) ? current( $package['rates'] ) : $package['rates'][ $chosen_initial_method ];
|
||||
// packages match, display shipping amounts only
|
||||
?>
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
* Description: Sell products and services with recurring payments in your WooCommerce Store.
|
||||
* Author: Prospress Inc.
|
||||
* Author URI: http://prospress.com/
|
||||
* Version: 2.0.18
|
||||
* Version: 2.0.19
|
||||
*
|
||||
* Copyright 2016 Prospress, Inc. (email : freedoms@prospress.com)
|
||||
*
|
||||
@@ -118,7 +118,7 @@ class WC_Subscriptions {
|
||||
|
||||
public static $plugin_file = __FILE__;
|
||||
|
||||
public static $version = '2.0.18';
|
||||
public static $version = '2.0.19';
|
||||
|
||||
private static $total_subscription_count = null;
|
||||
|
||||
|
Reference in New Issue
Block a user