diff --git a/assets/js/admin/admin.js b/assets/js/admin/admin.js index 0dee5cc..3eaf716 100644 --- a/assets/js/admin/admin.js +++ b/assets/js/admin/admin.js @@ -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(''); @@ -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(); + }); }); diff --git a/changelog.txt b/changelog.txt index 2f97ba4..4971916 100644 --- a/changelog.txt +++ b/changelog.txt @@ -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) diff --git a/includes/abstracts/abstract-wcs-cache-manager.php b/includes/abstracts/abstract-wcs-cache-manager.php index 96a04ae..b3b2500 100644 --- a/includes/abstracts/abstract-wcs-cache-manager.php +++ b/includes/abstracts/abstract-wcs-cache-manager.php @@ -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. diff --git a/includes/admin/class-wc-subscriptions-admin.php b/includes/admin/class-wc-subscriptions-admin.php index efcce7f..b7bcdb0 100644 --- a/includes/admin/class-wc-subscriptions-admin.php +++ b/includes/admin/class-wc-subscriptions-admin.php @@ -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 * @@ -664,14 +696,15 @@ class WC_Subscriptions_Admin { $dependencies[] = 'wc-admin-variation-meta-boxes'; $script_params = array( - 'productType' => WC_Subscriptions::$name, - 'trialPeriodSingular' => wcs_get_available_time_periods(), - 'trialPeriodPlurals' => wcs_get_available_time_periods( 'plural' ), - 'subscriptionLengths' => wcs_get_subscription_ranges(), - 'trialTooLongMessages' => self::get_trial_period_validation_message( 'separate' ), - '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' ), + 'productType' => WC_Subscriptions::$name, + 'trialPeriodSingular' => wcs_get_available_time_periods(), + 'trialPeriodPlurals' => wcs_get_available_time_periods( 'plural' ), + 'subscriptionLengths' => wcs_get_subscription_ranges(), + 'trialTooLongMessages' => self::get_trial_period_validation_message( 'separate' ), + '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( diff --git a/includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php b/includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php index 69c2502..6a2a505 100644 --- a/includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php +++ b/includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php @@ -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 ); diff --git a/includes/class-wc-subscription.php b/includes/class-wc-subscription.php index 298b7e8..a051e5a 100644 --- a/includes/class-wc-subscription.php +++ b/includes/class-wc-subscription.php @@ -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 ); diff --git a/includes/class-wc-subscriptions-cart.php b/includes/class-wc-subscriptions-cart.php index 1f53c03..0255599 100644 --- a/includes/class-wc-subscriptions-cart.php +++ b/includes/class-wc-subscriptions-cart.php @@ -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(); diff --git a/includes/class-wc-subscriptions-order.php b/includes/class-wc-subscriptions-order.php index 74bf251..20a9638 100644 --- a/includes/class-wc-subscriptions-order.php +++ b/includes/class-wc-subscriptions-order.php @@ -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( - 'key' => '_subscription_renewal', - 'compare' => 'EXISTS', - ), - ), - ) ); + if ( ! empty( $compare_operator ) ) { + $vars['meta_query'][] = array( + 'key' => '_subscription_renewal', + 'compare' => $compare_operator, + ); } } diff --git a/includes/class-wc-subscriptions-product.php b/includes/class-wc-subscriptions-product.php index bdde04d..b44ab5b 100644 --- a/includes/class-wc-subscriptions-product.php +++ b/includes/class-wc-subscriptions-product.php @@ -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; @@ -956,7 +964,7 @@ class WC_Subscriptions_Product { foreach ( $variation_ids as $variation_id ) { update_post_meta( $variation_id, $meta_key, stripslashes( $data['value'] ) ); } - } else if ( in_array( $meta_key, array( '_regular_price_increase', '_regular_price_decrease' ) ) ) { + } elseif ( in_array( $meta_key, array( '_regular_price_increase', '_regular_price_decrease' ) ) ) { $operator = ( '_regular_price_increase' == $meta_key ) ? '+' : '-'; $value = wc_clean( $data['value'] ); @@ -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. diff --git a/includes/class-wc-subscriptions-switcher.php b/includes/class-wc-subscriptions-switcher.php index f62e1f9..204ab60 100644 --- a/includes/class-wc-subscriptions-switcher.php +++ b/includes/class-wc-subscriptions-switcher.php @@ -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,8 +248,10 @@ class WC_Subscriptions_Switcher { } } - wp_redirect( add_query_arg( 'auto-switch', 'true', self::get_switch_url( $item_id, $item, $subscription ) ) ); - exit; + 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 { diff --git a/includes/class-wcs-cache-manager-tlc.php b/includes/class-wcs-cache-manager-tlc.php index 681dfa7..872829c 100644 --- a/includes/class-wcs-cache-manager-tlc.php +++ b/includes/class-wcs-cache-manager-tlc.php @@ -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(); } diff --git a/includes/class-wcs-query.php b/includes/class-wcs-query.php index a69e6b9..cb6141d 100644 --- a/includes/class-wcs-query.php +++ b/includes/class-wcs-query.php @@ -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 ); diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler.php b/includes/libraries/action-scheduler/classes/ActionScheduler.php index 8f34e64..fa08adf 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler.php @@ -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 ); + } } \ No newline at end of file diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_ActionFactory.php b/includes/libraries/action-scheduler/classes/ActionScheduler_ActionFactory.php index ef01287..f61ba48 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_ActionFactory.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_ActionFactory.php @@ -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 ); diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_AdminView.php b/includes/libraries/action-scheduler/classes/ActionScheduler_AdminView.php index 1259f21..f67f0e6 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_AdminView.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_AdminView.php @@ -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'] = "" . __( 'Run', 'action-scheduler' ) . ""; - } - - 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'] = "ID ) ), 'untrash-post_' . $post->ID ) . "'>" . __( 'Restore', 'action-scheduler' ) . ""; - } elseif ( EMPTY_TRASH_DAYS ) { - $actions['trash'] = "" . __( 'Trash', 'action-scheduler' ) . ""; - } - - if ( 'trash' == $post->post_status || !EMPTY_TRASH_DAYS ) { - $actions['delete'] = "" . __( 'Delete Permanently', 'action-scheduler' ) . ""; - } - } - - $action_count = count( $actions ); - $i = 0; - - echo '
'; - foreach ( $actions as $a => $link ) { - ++$i; - ( $i == $action_count ) ? $sep = '' : $sep = ' | '; - echo "$link$sep"; - } - echo '
'; - 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,8 +228,21 @@ class ActionScheduler_AdminView { */ public static function row_actions( $actions, $post ) { - if ( ActionScheduler_wpPostStore::POST_TYPE == $post->post_type && isset( $actions['edit'] ) ) { - unset( $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'] = "" . __( 'Run', 'action-scheduler' ) . ""; + } + + ksort( $actions ); } return $actions; diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_CronSchedule.php b/includes/libraries/action-scheduler/classes/ActionScheduler_CronSchedule.php index 33fc833..6e4af2d 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_CronSchedule.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_CronSchedule.php @@ -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); } } \ No newline at end of file diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_IntervalSchedule.php b/includes/libraries/action-scheduler/classes/ActionScheduler_IntervalSchedule.php index 246b1b6..3eae7bc 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_IntervalSchedule.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_IntervalSchedule.php @@ -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); } } \ No newline at end of file diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_QueueCleaner.php b/includes/libraries/action-scheduler/classes/ActionScheduler_QueueCleaner.php index 69a7171..cfcfa4b 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_QueueCleaner.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_QueueCleaner.php @@ -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, diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php b/includes/libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php index a125f9c..658e5fd 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php @@ -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 ); } diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_SimpleSchedule.php b/includes/libraries/action-scheduler/classes/ActionScheduler_SimpleSchedule.php index 7f33a45..58bf462 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_SimpleSchedule.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_SimpleSchedule.php @@ -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); } } \ No newline at end of file diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_wpCommentLogger.php b/includes/libraries/action-scheduler/classes/ActionScheduler_wpCommentLogger.php index b7aa925..aaefa2e 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_wpCommentLogger.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_wpCommentLogger.php @@ -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, diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore.php b/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore.php index 189ca5a..af54a36 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore.php @@ -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 ) { diff --git a/includes/libraries/action-scheduler/functions.php b/includes/libraries/action-scheduler/functions.php index 38f5132..feb8f02 100644 --- a/includes/libraries/action-scheduler/functions.php +++ b/includes/libraries/action-scheduler/functions.php @@ -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 ); @@ -157,4 +157,32 @@ function wc_get_scheduled_actions( $args = array(), $return_format = OBJECT ) { } return $actions; -} \ No newline at end of file +} + +/** + * 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; +} diff --git a/includes/wcs-cart-functions.php b/includes/wcs-cart-functions.php index 19798c5..63386ec 100644 --- a/includes/wcs-cart-functions.php +++ b/includes/wcs-cart-functions.php @@ -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 ?> diff --git a/languages/woocommerce-subscriptions.pot b/languages/woocommerce-subscriptions.pot index 4aeb791..7991160 100644 --- a/languages/woocommerce-subscriptions.pot +++ b/languages/woocommerce-subscriptions.pot @@ -2,10 +2,10 @@ # This file is distributed under the same license as the WooCommerce Subscriptions package. msgid "" msgstr "" -"Project-Id-Version: WooCommerce Subscriptions 2.0.17\n" +"Project-Id-Version: WooCommerce Subscriptions 2.0.19\n" "Report-Msgid-Bugs-To: " "https://github.com/Prospress/woocommerce-subscriptions/issues\n" -"POT-Creation-Date: 2016-06-24 20:31:33+00:00\n" +"POT-Creation-Date: 2016-08-12 22:45:52+00:00\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -15,16 +15,16 @@ msgstr "" "X-Generator: grunt-wp-i18n 0.5.4\n" "Language: en_US\n" -#: includes/admin/class-wc-subscriptions-admin.php:123 +#: includes/admin/class-wc-subscriptions-admin.php:125 msgid "Simple Subscription" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:124 +#: includes/admin/class-wc-subscriptions-admin.php:126 #: woocommerce-subscriptions.php:601 msgid "Variable Subscription" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:149 +#: includes/admin/class-wc-subscriptions-admin.php:151 #: templates/admin/deprecated/html-variation-price.php:20 #: templates/admin/deprecated/html-variation-price.php:30 #: templates/admin/html-variation-price.php:45 @@ -32,121 +32,127 @@ msgstr "" msgid "Subscription Price (%s)" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:162 +#: includes/admin/class-wc-subscriptions-admin.php:164 #: templates/admin/deprecated/html-variation-price.php:46 msgid "Subscription Periods" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:171 +#: includes/admin/class-wc-subscriptions-admin.php:173 #: includes/admin/meta-boxes/views/html-subscription-schedule.php:32 #: templates/admin/deprecated/html-variation-price.php:57 msgid "Billing Period" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:182 -#: includes/admin/class-wc-subscriptions-admin.php:323 +#: includes/admin/class-wc-subscriptions-admin.php:184 +#: includes/admin/class-wc-subscriptions-admin.php:325 #: templates/admin/deprecated/html-variation-price.php:69 msgid "Subscription Length" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:192 +#: includes/admin/class-wc-subscriptions-admin.php:194 #: templates/admin/deprecated/html-variation-price.php:85 #. translators: %s is a currency symbol / code msgid "Sign-up Fee (%s)" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:194 +#: includes/admin/class-wc-subscriptions-admin.php:196 msgid "" "Optionally include an amount to be charged at the outset of the " "subscription. The sign-up fee will be charged immediately, even if the " "product has a free trial or the payment dates are synced." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:207 +#: includes/admin/class-wc-subscriptions-admin.php:209 #: templates/admin/deprecated/html-variation-price.php:97 #: templates/admin/deprecated/html-variation-price.php:104 msgid "Free Trial" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:214 +#: includes/admin/class-wc-subscriptions-admin.php:216 #: templates/admin/deprecated/html-variation-price.php:115 msgid "Subscription Trial Period" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:244 +#: includes/admin/class-wc-subscriptions-admin.php:246 msgid "One Time Shipping" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:245 +#: includes/admin/class-wc-subscriptions-admin.php:247 msgid "" "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." -msgstr "" - -#: includes/admin/class-wc-subscriptions-admin.php:267 -msgid "Limit Subscription" +"initial order. Note: for this setting to be enabled the subscription must " +"not have a free trial or a synced renewal date." msgstr "" #: includes/admin/class-wc-subscriptions-admin.php:269 +msgid "Limit Subscription" +msgstr "" + +#: includes/admin/class-wc-subscriptions-admin.php:271 #. translators: placeholders are opening and closing link tags msgid "" "Only allow a customer to have one subscription to this product. %sLearn " "more%s." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:271 +#: includes/admin/class-wc-subscriptions-admin.php:273 msgid "Do not limit" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:272 +#: includes/admin/class-wc-subscriptions-admin.php:274 msgid "Limit to one active subscription" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:273 +#: includes/admin/class-wc-subscriptions-admin.php:275 msgid "Limit to one of any status" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:319 +#: includes/admin/class-wc-subscriptions-admin.php:321 msgid "Subscription Pricing" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:320 +#: includes/admin/class-wc-subscriptions-admin.php:322 msgid "Subscription Sign-up Fee" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:321 +#: includes/admin/class-wc-subscriptions-admin.php:323 msgid "Subscription Billing Interval" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:322 +#: includes/admin/class-wc-subscriptions-admin.php:324 msgid "Subscription Period" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:324 +#: includes/admin/class-wc-subscriptions-admin.php:326 msgid "Free Trial Length" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:325 +#: includes/admin/class-wc-subscriptions-admin.php:327 msgid "Free Trial Period" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:672 +#: includes/admin/class-wc-subscriptions-admin.php:650 +msgid "" +"Unable to change subscription status to \"%s\". Please assign a customer to " +"the subscription to activate it." +msgstr "" + +#: includes/admin/class-wc-subscriptions-admin.php:704 msgid "Enter the new period, either day, week, month or year:" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:673 +#: includes/admin/class-wc-subscriptions-admin.php:705 msgid "Enter a new length (e.g. 5):" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:674 +#: includes/admin/class-wc-subscriptions-admin.php:706 msgid "" "Enter a new interval as a single number (e.g. to charge every 2nd month, " "enter 2):" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:678 +#: includes/admin/class-wc-subscriptions-admin.php:711 msgid "" "You are about to trash one or more orders which contain a subscription.\n" "\n" @@ -154,13 +160,13 @@ msgid "" "orders." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:689 +#: includes/admin/class-wc-subscriptions-admin.php:722 msgid "" "Trashing this order will also trash the subscription purchased with the " "order." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:690 +#: includes/admin/class-wc-subscriptions-admin.php:723 msgid "" "WARNING: Bad things are about to happen!\n" "\n" @@ -172,13 +178,13 @@ msgid "" "gateway." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:691 +#: includes/admin/class-wc-subscriptions-admin.php:724 msgid "" "You are deleting a subscription item. You will also need to manually cancel " "and trash the subscription on the Manage Subscriptions screen." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:698 +#: includes/admin/class-wc-subscriptions-admin.php:731 msgid "" "Warning: Deleting a user will also delete the user's subscriptions. The " "user's orders will remain but be reassigned to the 'Guest' user.\n" @@ -187,28 +193,28 @@ msgid "" "subscriptions?" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:761 +#: includes/admin/class-wc-subscriptions-admin.php:794 msgid "Active Subscriber?" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:804 +#: includes/admin/class-wc-subscriptions-admin.php:837 msgid "Manage Subscriptions" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:808 +#: includes/admin/class-wc-subscriptions-admin.php:841 #: woocommerce-subscriptions.php:209 msgid "Search Subscriptions" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:828 -#: includes/admin/class-wc-subscriptions-admin.php:924 +#: includes/admin/class-wc-subscriptions-admin.php:861 +#: includes/admin/class-wc-subscriptions-admin.php:957 #: includes/class-wcs-query.php:90 includes/class-wcs-query.php:110 #: includes/class-wcs-query.php:112 woocommerce-subscriptions.php:200 #: woocommerce-subscriptions.php:213 msgid "Subscriptions" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:971 +#: includes/admin/class-wc-subscriptions-admin.php:1004 #. translators: $1-2: opening and closing tags of a link that takes to PayPal #. settings, $3-4: opening and closing tags of a link that takes to Woo #. marketplace / Stripe product page @@ -218,47 +224,47 @@ msgid "" "%3$sfree Stripe extension%4$s if you want to process automatic payments." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:974 +#: includes/admin/class-wc-subscriptions-admin.php:1007 #. translators: placeholder is name of a gateway msgid "The %s gateway can process automatic subscription payments." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:977 +#: includes/admin/class-wc-subscriptions-admin.php:1010 #. translators: %1$s - a comma separated list of gateway names (e.g. "stripe, #. paypal, worldpay"), %2$s - one name of gateway (e.g. "authorize.net") msgid "The %1$s & %2$s gateways can process automatic subscription payments." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:983 +#: includes/admin/class-wc-subscriptions-admin.php:1016 msgid "Button Text" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:990 +#: includes/admin/class-wc-subscriptions-admin.php:1023 msgid "Add to Cart Button Text" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:991 +#: includes/admin/class-wc-subscriptions-admin.php:1024 msgid "" "A product displays a button with the text \"Add to Cart\". By default, a " "subscription changes this to \"Sign Up Now\". You can customise the button " "text for subscriptions here." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:995 -#: includes/admin/class-wc-subscriptions-admin.php:1006 +#: includes/admin/class-wc-subscriptions-admin.php:1028 +#: includes/admin/class-wc-subscriptions-admin.php:1039 #: includes/class-wc-product-subscription-variation.php:75 #: includes/class-wc-product-subscription.php:115 #: includes/class-wc-product-variable-subscription.php:101 -#: includes/class-wc-subscriptions-product.php:124 +#: includes/class-wc-subscriptions-product.php:130 #: woocommerce-subscriptions.php:456 woocommerce-subscriptions.php:1110 msgid "Sign Up Now" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1001 +#: includes/admin/class-wc-subscriptions-admin.php:1034 msgid "Place Order Button Text" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1002 +#: includes/admin/class-wc-subscriptions-admin.php:1035 msgid "" "Use this field to customise the text displayed on the checkout button when " "an order contains a subscription. Normally the checkout submission button " @@ -266,11 +272,11 @@ msgid "" "changed to \"Sign Up Now\"." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1014 +#: includes/admin/class-wc-subscriptions-admin.php:1047 msgid "Roles" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1017 +#: includes/admin/class-wc-subscriptions-admin.php:1050 #. translators: placeholders are tags msgid "" "Choose the default roles to assign to active and inactive subscribers. For " @@ -279,46 +285,46 @@ msgid "" "allocated these roles to prevent locking out administrators." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1022 +#: includes/admin/class-wc-subscriptions-admin.php:1055 msgid "Subscriber Default Role" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1023 +#: includes/admin/class-wc-subscriptions-admin.php:1056 msgid "" "When a subscription is activated, either manually or after a successful " "purchase, new users will be assigned this role." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1034 +#: includes/admin/class-wc-subscriptions-admin.php:1067 msgid "Inactive Subscriber Role" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1035 +#: includes/admin/class-wc-subscriptions-admin.php:1068 msgid "" "If a subscriber's subscription is manually cancelled or expires, she will " "be assigned this role." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1055 +#: includes/admin/class-wc-subscriptions-admin.php:1088 msgid "Manual Renewal Payments" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1056 +#: includes/admin/class-wc-subscriptions-admin.php:1089 msgid "Accept Manual Renewals" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1061 +#: includes/admin/class-wc-subscriptions-admin.php:1094 #. translators: placeholders are opening and closing link tags msgid "" "With manual renewals, a customer's subscription is put on-hold until they " "login and pay to renew it. %sLearn more%s." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1067 +#: includes/admin/class-wc-subscriptions-admin.php:1100 msgid "Turn off Automatic Payments" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1072 +#: includes/admin/class-wc-subscriptions-admin.php:1105 #. translators: placeholders are opening and closing link tags msgid "" "If you never want a customer to be automatically charged for a subscription " @@ -326,11 +332,11 @@ msgid "" "more%s." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1087 +#: includes/admin/class-wc-subscriptions-admin.php:1120 msgid "Customer Suspensions" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1094 +#: includes/admin/class-wc-subscriptions-admin.php:1127 msgid "" "Set a maximum number of times a customer can suspend their account for each " "billing period. For example, for a value of 3 and a subscription billed " @@ -340,28 +346,28 @@ msgid "" "Set this to 0 to turn off the customer suspension feature completely." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1098 +#: includes/admin/class-wc-subscriptions-admin.php:1131 msgid "Mixed Checkout" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1099 +#: includes/admin/class-wc-subscriptions-admin.php:1132 msgid "Allow subscriptions and products to be purchased simultaneously." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1103 +#: includes/admin/class-wc-subscriptions-admin.php:1136 msgid "Allow subscriptions and products to be purchased in a single transaction." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1107 +#: includes/admin/class-wc-subscriptions-admin.php:1140 #: includes/upgrades/templates/wcs-about.php:108 msgid "Drip Downloadable Content" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1108 +#: includes/admin/class-wc-subscriptions-admin.php:1141 msgid "Enable dripping for downloadable content on subscription products." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1112 +#: includes/admin/class-wc-subscriptions-admin.php:1145 msgid "" "Enabling this grants access to new downloadable files added to a product " "only after the next renewal is processed.%sBy default, access to new " @@ -369,18 +375,18 @@ msgid "" "customer that has an active subscription with that product." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1118 +#: includes/admin/class-wc-subscriptions-admin.php:1151 msgid "Payment Gateways" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1126 +#: includes/admin/class-wc-subscriptions-admin.php:1159 #. translators: placeholders are opening and closing link tags msgid "" "Other payment gateways can be used to process %smanual subscription renewal " "payments%s only." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1133 +#: includes/admin/class-wc-subscriptions-admin.php:1166 #. translators: $1-$2: opening and closing tags. Link to documents->payment #. gateways, 3$-4$: opening and closing tags. Link to woothemes extensions shop #. page @@ -389,7 +395,7 @@ msgid "" "the official %3$sWooCommerce Marketplace%4$s." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1170 +#: includes/admin/class-wc-subscriptions-admin.php:1203 #. translators: $1-$2: opening and closing tags, $3-$4: opening and #. closing tags msgid "" @@ -397,52 +403,52 @@ msgid "" "start selling subscriptions!%4$s" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1175 +#: includes/admin/class-wc-subscriptions-admin.php:1208 msgid "Add a Subscription Product" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1176 +#: includes/admin/class-wc-subscriptions-admin.php:1209 #: includes/upgrades/templates/wcs-about.php:35 #: woocommerce-subscriptions.php:960 msgid "Settings" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1262 +#: includes/admin/class-wc-subscriptions-admin.php:1295 #. translators: placeholder is a number msgid "We can't find a subscription with ID #%d. Perhaps it was deleted?" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1295 -#: includes/admin/class-wc-subscriptions-admin.php:1300 +#: includes/admin/class-wc-subscriptions-admin.php:1328 +#: includes/admin/class-wc-subscriptions-admin.php:1333 #. translators: placeholders are opening link tag, ID of sub, and closing link #. tag msgid "Showing orders for %sSubscription %s%s" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1324 +#: includes/admin/class-wc-subscriptions-admin.php:1357 #. translators: number of 1$: days, 2$: weeks, 3$: months, 4$: years msgid "The trial period can not exceed: %1s, %2s, %3s or %4s." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1329 +#: includes/admin/class-wc-subscriptions-admin.php:1362 #. translators: placeholder is a time period (e.g. "4 weeks") msgid "The trial period can not exceed %s." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1383 -#: includes/admin/class-wc-subscriptions-admin.php:1436 +#: includes/admin/class-wc-subscriptions-admin.php:1418 +#: includes/admin/class-wc-subscriptions-admin.php:1471 msgid "Yes" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1383 +#: includes/admin/class-wc-subscriptions-admin.php:1418 msgid "No" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1419 +#: includes/admin/class-wc-subscriptions-admin.php:1454 msgid "Automatic Recurring Payments" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1436 +#: includes/admin/class-wc-subscriptions-admin.php:1471 msgid "" "Supports automatic renewal payments with the WooCommerce Subscriptions " "extension." @@ -503,18 +509,18 @@ msgstr "" msgid "Create pending renewal order requested by admin action." msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:111 +#: includes/admin/class-wcs-admin-post-types.php:114 msgid "Search for a product…" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:284 +#: includes/admin/class-wcs-admin-post-types.php:287 #. translators: placeholder is the number of subscriptions updated msgid "%s subscription status changed." msgid_plural "%s subscription statuses changed." msgstr[0] "" msgstr[1] "" -#: includes/admin/class-wcs-admin-post-types.php:291 +#: includes/admin/class-wcs-admin-post-types.php:294 #. translators: 1$: is the number of subscriptions not updated, 2$: is the #. error message msgid "%1$s subscription could not be updated: %2$s" @@ -522,7 +528,7 @@ msgid_plural "%1$s subscriptions could not be updated: %2$s" msgstr[0] "" msgstr[1] "" -#: includes/admin/class-wcs-admin-post-types.php:313 +#: includes/admin/class-wcs-admin-post-types.php:316 #: includes/admin/meta-boxes/views/html-related-orders-table.php:20 #: templates/myaccount/my-subscriptions.php:26 #: templates/myaccount/my-subscriptions.php:40 @@ -534,7 +540,7 @@ msgstr[1] "" msgid "Status" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:314 +#: includes/admin/class-wcs-admin-post-types.php:317 #: includes/admin/meta-boxes/class-wcs-meta-box-related-orders.php:61 #: templates/emails/cancelled-subscription.php:26 #: templates/emails/subscription-info.php:18 @@ -544,77 +550,77 @@ msgstr "" msgid "Subscription" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:315 +#: includes/admin/class-wcs-admin-post-types.php:318 msgid "Items" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:316 +#: includes/admin/class-wcs-admin-post-types.php:319 msgid "Total" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:317 +#: includes/admin/class-wcs-admin-post-types.php:320 msgid "Start Date" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:318 +#: includes/admin/class-wcs-admin-post-types.php:321 msgid "Trial End" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:319 +#: includes/admin/class-wcs-admin-post-types.php:322 msgid "Next Payment" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:320 +#: includes/admin/class-wcs-admin-post-types.php:323 msgid "Last Payment" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:321 +#: includes/admin/class-wcs-admin-post-types.php:324 msgid "End Date" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:364 +#: includes/admin/class-wcs-admin-post-types.php:365 #: includes/wcs-user-functions.php:272 msgid "Reactivate" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:365 +#: includes/admin/class-wcs-admin-post-types.php:366 #: includes/wcs-user-functions.php:267 msgid "Suspend" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:367 -#: includes/admin/class-wcs-admin-post-types.php:382 +#: includes/admin/class-wcs-admin-post-types.php:368 +#: includes/admin/class-wcs-admin-post-types.php:383 msgid "Trash" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:368 -#: includes/admin/class-wcs-admin-post-types.php:386 +#: includes/admin/class-wcs-admin-post-types.php:369 +#: includes/admin/class-wcs-admin-post-types.php:387 msgid "Delete Permanently" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:380 -#: includes/class-wc-subscriptions-product.php:805 +#: includes/admin/class-wcs-admin-post-types.php:381 +#: includes/class-wc-subscriptions-product.php:811 msgid "Restore this item from the Trash" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:380 -#: includes/class-wc-subscriptions-product.php:806 +#: includes/admin/class-wcs-admin-post-types.php:381 +#: includes/class-wc-subscriptions-product.php:812 msgid "Restore" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:382 +#: includes/admin/class-wcs-admin-post-types.php:383 msgid "Move this item to the Trash" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:386 +#: includes/admin/class-wcs-admin-post-types.php:387 msgid "Delete this item permanently" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:392 +#: includes/admin/class-wcs-admin-post-types.php:393 msgid "Cancel Now" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:425 +#: includes/admin/class-wcs-admin-post-types.php:426 #: templates/emails/plain/admin-new-renewal-order.php:54 #: templates/emails/plain/customer-completed-renewal-order.php:52 #: templates/emails/plain/customer-processing-renewal-order.php:51 @@ -622,7 +628,7 @@ msgstr "" msgid "Email: %s" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:430 +#: includes/admin/class-wcs-admin-post-types.php:431 #: templates/emails/plain/admin-new-renewal-order.php:59 #: templates/emails/plain/customer-completed-renewal-order.php:58 #: templates/emails/plain/customer-processing-renewal-order.php:57 @@ -630,65 +636,69 @@ msgstr "" msgid "Tel: %s" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:504 +#: includes/admin/class-wcs-admin-post-types.php:463 +msgid "Show more details" +msgstr "" + +#: includes/admin/class-wcs-admin-post-types.php:507 msgid "%d item" msgid_plural "%d items" msgstr[0] "" msgstr[1] "" -#: includes/admin/class-wcs-admin-post-types.php:539 +#: includes/admin/class-wcs-admin-post-types.php:542 #: templates/myaccount/my-subscriptions.php:48 #. translators: placeholder is the display name of a payment gateway a #. subscription was paid by msgid "Via %s" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:550 +#: includes/admin/class-wcs-admin-post-types.php:553 msgid "Y/m/d g:i:s A" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:553 +#: includes/admin/class-wcs-admin-post-types.php:556 msgid "" "This date should be treated as an estimate only. The payment gateway for " "this subscription controls when payments are processed." msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:826 #: includes/admin/class-wcs-admin-post-types.php:829 #: includes/admin/class-wcs-admin-post-types.php:832 +#: includes/admin/class-wcs-admin-post-types.php:835 msgid "Subscription updated." msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:827 +#: includes/admin/class-wcs-admin-post-types.php:830 msgid "Custom field updated." msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:828 +#: includes/admin/class-wcs-admin-post-types.php:831 msgid "Custom field deleted." msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:833 +#: includes/admin/class-wcs-admin-post-types.php:836 msgid "Subscription saved." msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:834 +#: includes/admin/class-wcs-admin-post-types.php:837 msgid "Subscription submitted." msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:836 +#: includes/admin/class-wcs-admin-post-types.php:839 #. translators: php date string msgid "Subscription scheduled for: %1$s." msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:837 +#: includes/admin/class-wcs-admin-post-types.php:840 msgid "Subscription draft updated." msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:875 +#: includes/admin/class-wcs-admin-post-types.php:878 msgid "Any Payment Method" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:876 +#: includes/admin/class-wcs-admin-post-types.php:879 msgid "None" msgstr "" @@ -749,18 +759,18 @@ msgstr "" #: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:275 #. translators: placeholder is error message from the payment gateway or #. subscriptions when updating the status -msgid "Error updating subscription: %s" +msgid "Error updating some information: %s" msgstr "" #: includes/admin/meta-boxes/views/html-related-orders-row.php:34 -#: includes/class-wc-subscription.php:679 +#: includes/class-wc-subscription.php:681 #: includes/class-wc-subscriptions-manager.php:2328 #. translators: placeholder is human time diff (e.g. "3 weeks") msgid "In %s" msgstr "" #: includes/admin/meta-boxes/views/html-related-orders-row.php:37 -#: includes/class-wc-subscription.php:682 +#: includes/class-wc-subscription.php:684 #. translators: placeholder is human time diff (e.g. "3 weeks") msgid "%s ago" msgstr "" @@ -836,83 +846,83 @@ msgstr "" msgid "Invalid subscription billing period given." msgstr "" -#: includes/class-wc-subscription.php:307 -#: includes/class-wc-subscription.php:398 +#: includes/class-wc-subscription.php:309 +#: includes/class-wc-subscription.php:400 msgid "Unable to change subscription status to \"%s\"." msgstr "" -#: includes/class-wc-subscription.php:380 +#: includes/class-wc-subscription.php:382 #. translators: $1 note why the status changes (if any), $2: old status, $3: #. new status msgid "%1$s Status changed from %2$s to %3$s." msgstr "" -#: includes/class-wc-subscription.php:689 +#: includes/class-wc-subscription.php:691 msgid "Not yet ended" msgstr "" -#: includes/class-wc-subscription.php:732 +#: includes/class-wc-subscription.php:734 msgid "Invalid format. First parameter needs to be an array." msgstr "" -#: includes/class-wc-subscription.php:736 +#: includes/class-wc-subscription.php:738 msgid "Invalid data. First parameter was empty when passed to update_dates()." msgstr "" -#: includes/class-wc-subscription.php:743 +#: includes/class-wc-subscription.php:745 msgid "" "Invalid data. First parameter has a date that is not in the registered date " "types." msgstr "" -#: includes/class-wc-subscription.php:791 +#: includes/class-wc-subscription.php:793 msgid "The %s date must occur after the last payment date." msgstr "" -#: includes/class-wc-subscription.php:795 +#: includes/class-wc-subscription.php:797 msgid "The %s date must occur after the next payment date." msgstr "" -#: includes/class-wc-subscription.php:800 +#: includes/class-wc-subscription.php:802 msgid "The %s date must occur after the trial end date." msgstr "" -#: includes/class-wc-subscription.php:804 +#: includes/class-wc-subscription.php:806 msgid "The %s date must occur after the start date." msgstr "" -#: includes/class-wc-subscription.php:859 +#: includes/class-wc-subscription.php:861 msgid "The start date of a subscription can not be deleted, only updated." msgstr "" -#: includes/class-wc-subscription.php:862 +#: includes/class-wc-subscription.php:864 msgid "" "The last payment date of a subscription can not be deleted. You must delete " "the order." msgstr "" -#: includes/class-wc-subscription.php:1263 +#: includes/class-wc-subscription.php:1267 msgid "Sign-up complete." msgstr "" -#: includes/class-wc-subscription.php:1265 +#: includes/class-wc-subscription.php:1269 msgid "Payment received." msgstr "" -#: includes/class-wc-subscription.php:1296 +#: includes/class-wc-subscription.php:1300 msgid "Payment failed." msgstr "" -#: includes/class-wc-subscription.php:1300 +#: includes/class-wc-subscription.php:1304 msgid "Subscription Cancelled: maximum number of failed payments reached." msgstr "" -#: includes/class-wc-subscription.php:1495 +#: includes/class-wc-subscription.php:1499 #: includes/class-wcs-change-payment-method-admin.php:155 msgid "Manual Renewal" msgstr "" -#: includes/class-wc-subscription.php:1560 +#: includes/class-wc-subscription.php:1564 msgid "Payment method meta must be an array." msgstr "" @@ -932,21 +942,21 @@ msgstr "" msgid "Update the %1$s used for %2$sall%3$s of my active subscriptions" msgstr "" -#: includes/class-wc-subscriptions-cart.php:864 +#: includes/class-wc-subscriptions-cart.php:823 msgid "Please enter a valid postcode/ZIP." msgstr "" -#: includes/class-wc-subscriptions-cart.php:1035 +#: includes/class-wc-subscriptions-cart.php:994 msgid "" "That subscription product can not be added to your cart as it already " "contains a subscription renewal." msgstr "" -#: includes/class-wc-subscriptions-cart.php:1116 +#: includes/class-wc-subscriptions-cart.php:1079 msgid "Invalid recurring shipping method." msgstr "" -#: includes/class-wc-subscriptions-cart.php:2002 +#: includes/class-wc-subscriptions-cart.php:2014 msgid "now" msgstr "" @@ -1184,12 +1194,12 @@ msgstr[1] "" msgid "Show all types" msgstr "" -#: includes/class-wc-subscriptions-order.php:884 +#: includes/class-wc-subscriptions-order.php:874 #. translators: $1: opening link tag, $2: order number, $3: closing link tag msgid "Subscription cancelled for refunded order %1$s#%2$s%3$s." msgstr "" -#: includes/class-wc-subscriptions-product.php:345 +#: includes/class-wc-subscriptions-product.php:351 #: includes/wcs-formatting-functions.php:102 #: includes/wcs-formatting-functions.php:186 #. translators: 1$: recurring amount string, 2$: day of the week (e.g. "$10 @@ -1197,34 +1207,34 @@ msgstr "" msgid "%1$s every %2$s" msgstr "" -#: includes/class-wc-subscriptions-product.php:348 +#: includes/class-wc-subscriptions-product.php:354 #: includes/wcs-formatting-functions.php:111 #. translators: 1$: recurring amount string, 2$: period, 3$: day of the week #. (e.g. "$10 every 2nd week on Wednesday") msgid "%1$s every %2$s on %3$s" msgstr "" -#: includes/class-wc-subscriptions-product.php:355 +#: includes/class-wc-subscriptions-product.php:361 #: includes/wcs-formatting-functions.php:129 #. translators: placeholder is recurring amount msgid "%s on the last day of each month" msgstr "" -#: includes/class-wc-subscriptions-product.php:358 +#: includes/class-wc-subscriptions-product.php:364 #: includes/wcs-formatting-functions.php:132 #. translators: 1$: recurring amount, 2$: day of the month (e.g. "23rd") (e.g. #. "$5 every 23rd of each month") msgid "%1$s on the %2$s of each month" msgstr "" -#: includes/class-wc-subscriptions-product.php:363 +#: includes/class-wc-subscriptions-product.php:369 #: includes/wcs-formatting-functions.php:148 #. translators: 1$: recurring amount, 2$: interval (e.g. "3rd") (e.g. "$10 on #. the last day of every 3rd month") msgid "%1$s on the last day of every %2$s month" msgstr "" -#: includes/class-wc-subscriptions-product.php:366 +#: includes/class-wc-subscriptions-product.php:372 #: includes/wcs-formatting-functions.php:151 #. translators: 1$: on the, 2$: day of every, 3$: #. month (e.g. "$10 on the 23rd day of every 2nd month") @@ -1233,7 +1243,7 @@ msgstr "" msgid "%1$s on the %2$s day of every %3$s month" msgstr "" -#: includes/class-wc-subscriptions-product.php:373 +#: includes/class-wc-subscriptions-product.php:379 #: includes/wcs-formatting-functions.php:164 #. translators: 1$: on, 2$: , 3$: each year (e.g. "$15 on #. March 15th each year") @@ -1242,14 +1252,14 @@ msgstr "" msgid "%1$s on %2$s %3$s each year" msgstr "" -#: includes/class-wc-subscriptions-product.php:376 +#: includes/class-wc-subscriptions-product.php:382 #: includes/wcs-formatting-functions.php:173 #. translators: 1$: recurring amount, 2$: month (e.g. "March"), 3$: day of the #. month (e.g. "23rd") (e.g. "$15 on March 15th every 3rd year") msgid "%1$s on %2$s %3$s every %4$s year" msgstr "" -#: includes/class-wc-subscriptions-product.php:382 +#: includes/class-wc-subscriptions-product.php:388 #: includes/wcs-formatting-functions.php:184 #. translators: 1$: recurring amount, 2$: subscription period (e.g. "month" or #. "3 months") (e.g. "$15 / month" or "$15 every 2nd month") @@ -1258,52 +1268,52 @@ msgid_plural " %1$s every %2$s" msgstr[0] "" msgstr[1] "" -#: includes/class-wc-subscriptions-product.php:388 +#: includes/class-wc-subscriptions-product.php:394 #. translators: billing period (e.g. "every week") msgid "every %s" msgstr "" -#: includes/class-wc-subscriptions-product.php:394 +#: includes/class-wc-subscriptions-product.php:400 #: includes/wcs-formatting-functions.php:194 #. translators: 1$: subscription string (e.g. "$10 up front then $5 on March #. 23rd every 3rd year"), 2$: length (e.g. "4 years") msgid "%1$s for %2$s" msgstr "" -#: includes/class-wc-subscriptions-product.php:400 +#: includes/class-wc-subscriptions-product.php:406 #. translators: 1$: subscription string (e.g. "$15 on March 15th every 3 years #. for 6 years"), 2$: trial length (e.g.: "with 4 months free trial") msgid "%1$s with %2$s free trial" msgstr "" -#: includes/class-wc-subscriptions-product.php:405 +#: includes/class-wc-subscriptions-product.php:411 #. translators: 1$: subscription string (e.g. "$15 on March 15th every 3 years #. for 6 years with 2 months free trial"), 2$: signup fee price (e.g. "and a #. $30 sign-up fee") msgid "%1$s and a %2$s sign-up fee" msgstr "" -#: includes/class-wc-subscriptions-renewal-order.php:139 +#: includes/class-wc-subscriptions-renewal-order.php:140 #. translators: placeholder is order ID msgid "Order %s created to record renewal." msgstr "" -#: includes/class-wc-subscriptions-renewal-order.php:159 +#: includes/class-wc-subscriptions-renewal-order.php:160 msgid "Subscription renewal orders cannot be cancelled." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:143 +#: includes/class-wc-subscriptions-switcher.php:155 msgid "" "You have a subscription to this product. Choosing a new subscription will " "replace your existing subscription." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:145 +#: includes/class-wc-subscriptions-switcher.php:157 msgid "Choose a new subscription." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:166 -#: includes/class-wc-subscriptions-switcher.php:842 +#: includes/class-wc-subscriptions-switcher.php:179 +#: includes/class-wc-subscriptions-switcher.php:857 msgid "" "Your cart contained an invalid subscription switch request. It has been " "removed." @@ -1313,13 +1323,13 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: includes/class-wc-subscriptions-switcher.php:207 +#: includes/class-wc-subscriptions-switcher.php:220 msgid "" "You have already subscribed to this product and it is limited to one per " "customer. You can not purchase the product again." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:219 +#: includes/class-wc-subscriptions-switcher.php:232 #. translators: 1$: is the "You have already subscribed to this product" #. notice, 2$-4$: opening/closing link tags, 3$: an order number msgid "" @@ -1327,101 +1337,101 @@ msgid "" "subscription." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:303 +#: includes/class-wc-subscriptions-switcher.php:318 msgid "Switching" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:306 +#: includes/class-wc-subscriptions-switcher.php:321 #. translators: placeholders are opening and closing link tags msgid "" "Allow subscribers to switch (upgrade or downgrade) between different " "subscriptions. %sLearn more%s." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:311 +#: includes/class-wc-subscriptions-switcher.php:326 msgid "Allow Switching" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:312 +#: includes/class-wc-subscriptions-switcher.php:327 msgid "" "Allow subscribers to switch between subscriptions combined in a grouped " "product, different variations of a Variable subscription or don't allow " "switching." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:328 +#: includes/class-wc-subscriptions-switcher.php:343 msgid "Prorate Recurring Payment" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:329 +#: includes/class-wc-subscriptions-switcher.php:344 msgid "" "When switching to a subscription with a different recurring payment or " "billing period, should the price paid for the existing billing period be " "prorated when switching to the new subscription?" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:346 +#: includes/class-wc-subscriptions-switcher.php:361 msgid "Prorate Sign up Fee" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:347 +#: includes/class-wc-subscriptions-switcher.php:362 msgid "" "When switching to a subscription with a sign up fee, you can require the " "customer pay only the gap between the existing subscription's sign up fee " "and the new subscription's sign up fee (if any)." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:362 +#: includes/class-wc-subscriptions-switcher.php:377 msgid "Prorate Subscription Length" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:363 +#: includes/class-wc-subscriptions-switcher.php:378 msgid "" "When switching to a subscription with a length, you can take into account " "the payments already completed by the customer when determining how many " "payments the subscriber needs to make for the new subscription." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:378 +#: includes/class-wc-subscriptions-switcher.php:393 msgid "Switch Button Text" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:379 +#: includes/class-wc-subscriptions-switcher.php:394 msgid "" "Customise the text displayed on the button next to the subscription on the " "subscriber's account page. The default is \"Switch Subscription\", but you " "may wish to change this to \"Upgrade\" or \"Change Subscription\"." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:383 -#: includes/class-wc-subscriptions-switcher.php:409 -#: includes/class-wc-subscriptions-switcher.php:1817 +#: includes/class-wc-subscriptions-switcher.php:398 +#: includes/class-wc-subscriptions-switcher.php:424 +#: includes/class-wc-subscriptions-switcher.php:1858 msgid "Upgrade or Downgrade" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:795 +#: includes/class-wc-subscriptions-switcher.php:810 msgid "Switch Order" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:810 +#: includes/class-wc-subscriptions-switcher.php:825 msgid "Switched Subscription" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:910 +#: includes/class-wc-subscriptions-switcher.php:925 msgid "We can not find your old subscription item." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:924 +#: includes/class-wc-subscriptions-switcher.php:939 msgid "You can not switch to the same subscription." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:971 +#: includes/class-wc-subscriptions-switcher.php:986 msgid "" "You can not switch this subscription. It appears you do not own the " "subscription." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:1006 +#: includes/class-wc-subscriptions-switcher.php:1021 msgid "There was an error locating the switch details." msgstr "" @@ -1535,7 +1545,7 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: includes/class-wcs-cart-renewal.php:534 +#: includes/class-wcs-cart-renewal.php:540 msgid "All linked subscription items have been removed from the cart." msgstr "" @@ -2307,7 +2317,7 @@ msgstr "" msgid "There was an error with the update. Please refresh the page and try again." msgstr "" -#: includes/wcs-cart-functions.php:72 +#: includes/wcs-cart-functions.php:73 msgid "Shipping via %s" msgstr "" @@ -2317,11 +2327,11 @@ msgid_plural "Shipping %d" msgstr[0] "" msgstr[1] "" -#: includes/wcs-cart-functions.php:219 +#: includes/wcs-cart-functions.php:220 msgid "Free shipping coupon" msgstr "" -#: includes/wcs-cart-functions.php:322 +#: includes/wcs-cart-functions.php:323 #. translators: placeholder is a date msgid "First renewal: %s" msgstr "" @@ -2480,6 +2490,7 @@ msgstr[0] "" msgstr[1] "" #: includes/wcs-user-functions.php:279 +#: templates/single-product/add-to-cart/subscription.php:42 #: templates/single-product/add-to-cart/variable-subscription.php:30 msgid "Resubscribe" msgstr "" @@ -2496,15 +2507,15 @@ msgstr "" msgid "Subscription Trial Period:" msgstr "" -#: templates/admin/html-variation-price.php:49 -msgid "Billing Period:" -msgstr "" - -#: templates/admin/html-variation-price.php:56 +#: templates/admin/html-variation-price.php:50 msgid "Billing Interval:" msgstr "" -#: templates/admin/html-variation-price.php:64 +#: templates/admin/html-variation-price.php:57 +msgid "Billing Period:" +msgstr "" + +#: templates/admin/html-variation-price.php:66 msgid "Subscription Length:" msgstr "" @@ -2762,10 +2773,6 @@ msgstr "" msgid "Shipping Address" msgstr "" -#: templates/single-product/add-to-cart/subscription.php:42 -msgid "Renew" -msgstr "" - #: templates/single-product/add-to-cart/subscription.php:45 #: templates/single-product/add-to-cart/variable-subscription.php:33 msgid "You have an active subscription to this product already." @@ -2957,8 +2964,8 @@ msgstr "" msgid "http://prospress.com/" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:150 -#: includes/admin/class-wc-subscriptions-admin.php:193 +#: includes/admin/class-wc-subscriptions-admin.php:152 +#: includes/admin/class-wc-subscriptions-admin.php:195 #: templates/admin/deprecated/html-variation-price.php:31 #: templates/admin/deprecated/html-variation-price.php:86 #: templates/admin/html-variation-price.php:21 @@ -2967,12 +2974,12 @@ msgctxt "example price" msgid "e.g. 9.90" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:173 +#: includes/admin/class-wc-subscriptions-admin.php:175 msgctxt "for in \"Every month _for_ 12 months\"" msgid "for" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:217 +#: includes/admin/class-wc-subscriptions-admin.php:219 #: templates/admin/deprecated/html-variation-price.php:118 #: templates/admin/html-variation-price.php:27 #. translators: placeholder is trial period validation message if passed an @@ -2984,7 +2991,7 @@ msgid "" "subscription. %s" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:716 +#: includes/admin/class-wc-subscriptions-admin.php:749 #. translators: placeholders are for HTML tags. They are 1$: "

", 2$: #. "

", 3$: "

", 4$: "", 5$: "", 6$: "", 7$: "", 8$: #. "

" @@ -2995,7 +3002,7 @@ msgid "" "%6$sVariable subscription%7$s.%8$s" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:718 +#: includes/admin/class-wc-subscriptions-admin.php:751 #. translators: placeholders are for HTML tags. They are 1$: "

", 2$: #. "

", 3$: "

", 4$: "

" msgctxt "used in admin pointer script params in javascript as price pointer content" @@ -3005,37 +3012,48 @@ msgid "" "sign-up fee and free trial.%4$s" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1048 +#: includes/admin/class-wc-subscriptions-admin.php:1081 msgctxt "option section heading" msgid "Renewals" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1080 +#: includes/admin/class-wc-subscriptions-admin.php:1113 msgctxt "options section heading" msgid "Miscellaneous" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1088 +#: includes/admin/class-wc-subscriptions-admin.php:1121 msgctxt "there's a number immediately in front of this text" msgid "suspensions per billing period." msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1382 +#: includes/admin/class-wc-subscriptions-admin.php:1388 +msgctxt "in [subscriptions] shortcode" +msgid "No subscriptions found." +msgstr "" + +#: includes/admin/class-wc-subscriptions-admin.php:1397 +#. translators: order number +msgctxt "in [subscriptions] shortcode" +msgid "Subscription %s" +msgstr "" + +#: includes/admin/class-wc-subscriptions-admin.php:1417 msgctxt "label that indicates whether debugging is turned on for the plugin" msgid "WCS_DEBUG" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1388 +#: includes/admin/class-wc-subscriptions-admin.php:1423 msgctxt "Live or Staging, Label on WooCommerce -> System Status page" msgid "Subscriptions Mode" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1389 +#: includes/admin/class-wc-subscriptions-admin.php:1424 msgctxt "refers to staging site" msgid "Staging" msgstr "" -#: includes/admin/class-wc-subscriptions-admin.php:1389 +#: includes/admin/class-wc-subscriptions-admin.php:1424 msgctxt "refers to live site" msgid "Live" msgstr "" @@ -3050,54 +3068,54 @@ msgctxt "meta box title" msgid "Billing Schedule" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:151 +#: includes/admin/class-wcs-admin-post-types.php:154 msgctxt "an action on a subscription" msgid "Activate" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:152 +#: includes/admin/class-wcs-admin-post-types.php:155 msgctxt "an action on a subscription" msgid "Put on-hold" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:153 -#: includes/admin/class-wcs-admin-post-types.php:366 +#: includes/admin/class-wcs-admin-post-types.php:156 +#: includes/admin/class-wcs-admin-post-types.php:367 #: includes/class-wc-subscriptions-manager.php:1762 -#: includes/wcs-user-functions.php:287 +#: includes/wcs-user-functions.php:288 #: templates/myaccount/related-orders.php:66 msgctxt "an action on a subscription" msgid "Cancel" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:228 +#: includes/admin/class-wcs-admin-post-types.php:231 msgctxt "Used in order note. Reason why status changed." msgid "Subscription status changed by bulk edit:" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:322 +#: includes/admin/class-wcs-admin-post-types.php:325 msgctxt "number of orders linked to a subscription" msgid "Orders" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:420 +#: includes/admin/class-wcs-admin-post-types.php:421 msgctxt "meaning billing address" msgid "Billing:" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:458 +#: includes/admin/class-wcs-admin-post-types.php:459 #. translators: $1: is opening link, $2: is subscription order number, $3: is #. closing link tag, $4: is user's name msgctxt "Subscription title on admin table. (e.g.: #211 for John Doe)" msgid "%1$s#%2$s%3$s for %4$s" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:831 +#: includes/admin/class-wcs-admin-post-types.php:834 #. translators: placeholder is previous post title msgctxt "used in post updated messages" msgid "Subscription restored to revision from %s" msgstr "" -#: includes/admin/class-wcs-admin-post-types.php:836 +#: includes/admin/class-wcs-admin-post-types.php:839 msgctxt "used in \"Subscription scheduled for \"" msgid "M j, Y @ G:i" msgstr "" @@ -3129,7 +3147,7 @@ msgid "Subscription #%s details" msgstr "" #: includes/admin/meta-boxes/views/html-related-orders-row.php:15 -#: includes/class-wc-subscriptions-renewal-order.php:136 +#: includes/class-wc-subscriptions-renewal-order.php:137 #: templates/myaccount/my-subscriptions.php:37 #: templates/myaccount/related-orders.php:38 #: templates/myaccount/related-subscriptions.php:32 @@ -3203,12 +3221,12 @@ msgctxt "API response confirming order note deleted from a subscription" msgid "Permanently deleted subscription note" msgstr "" -#: includes/class-wc-subscription.php:694 +#: includes/class-wc-subscription.php:696 msgctxt "original denotes there is no date to display" msgid "-" msgstr "" -#: includes/class-wc-subscription.php:750 +#: includes/class-wc-subscription.php:752 #. translators: placeholder is date type (e.g. "end", "next_payment"...) msgctxt "appears in an error message if date is wrong format" msgid "Invalid %s date. The date must be of the format: \"Y-m-d H:i:s\"." @@ -3268,7 +3286,7 @@ msgctxt "Subscription status" msgid "On-hold" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:1958 wcs-functions.php:208 +#: includes/class-wc-subscriptions-switcher.php:1999 wcs-functions.php:208 msgctxt "Subscription status" msgid "Switched" msgstr "" @@ -3300,111 +3318,111 @@ msgctxt "An order type" msgid "Renewal" msgstr "" -#: includes/class-wc-subscriptions-renewal-order.php:241 +#: includes/class-wc-subscriptions-renewal-order.php:242 #. translators: 1$: blog name, 2$: order number msgctxt "used in new renewal order email, deprecated" msgid "[%1$s] New Subscription Renewal Order (%2$s)" msgstr "" -#: includes/class-wc-subscriptions-renewal-order.php:263 -#: includes/class-wc-subscriptions-renewal-order.php:287 +#: includes/class-wc-subscriptions-renewal-order.php:264 +#: includes/class-wc-subscriptions-renewal-order.php:288 #. translators: placeholder is blog name msgctxt "used as email subject for renewal order notification email to customer" msgid "[%s] Subscription Renewal Order" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:319 -#: includes/class-wc-subscriptions-switcher.php:336 -#: includes/class-wc-subscriptions-switcher.php:370 +#: includes/class-wc-subscriptions-switcher.php:334 +#: includes/class-wc-subscriptions-switcher.php:351 +#: includes/class-wc-subscriptions-switcher.php:385 #: includes/class-wc-subscriptions-synchroniser.php:172 msgctxt "when to allow a setting" msgid "Never" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:320 +#: includes/class-wc-subscriptions-switcher.php:335 msgctxt "when to allow switching" msgid "Between Subscription Variations" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:321 +#: includes/class-wc-subscriptions-switcher.php:336 msgctxt "when to allow switching" msgid "Between Grouped Subscriptions" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:322 +#: includes/class-wc-subscriptions-switcher.php:337 msgctxt "when to allow switching" msgid "Between Both Variations & Grouped Subscriptions" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:337 +#: includes/class-wc-subscriptions-switcher.php:352 msgctxt "when to prorate recurring fee when switching" msgid "For Upgrades of Virtual Subscription Products Only" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:338 +#: includes/class-wc-subscriptions-switcher.php:353 msgctxt "when to prorate recurring fee when switching" msgid "For Upgrades of All Subscription Products" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:339 +#: includes/class-wc-subscriptions-switcher.php:354 msgctxt "when to prorate recurring fee when switching" msgid "For Upgrades & Downgrades of Virtual Subscription Products Only" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:340 +#: includes/class-wc-subscriptions-switcher.php:355 msgctxt "when to prorate recurring fee when switching" msgid "For Upgrades & Downgrades of All Subscription Products" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:354 +#: includes/class-wc-subscriptions-switcher.php:369 msgctxt "when to prorate signup fee when switching" msgid "Never (do not charge a sign up fee)" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:355 +#: includes/class-wc-subscriptions-switcher.php:370 msgctxt "when to prorate signup fee when switching" msgid "Never (charge the full sign up fee)" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:356 +#: includes/class-wc-subscriptions-switcher.php:371 msgctxt "when to prorate signup fee when switching" msgid "Always" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:371 +#: includes/class-wc-subscriptions-switcher.php:386 #: includes/class-wc-subscriptions-synchroniser.php:173 msgctxt "when to prorate first payment / subscription length" msgid "For Virtual Subscription Products Only" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:372 +#: includes/class-wc-subscriptions-switcher.php:387 #: includes/class-wc-subscriptions-synchroniser.php:174 msgctxt "when to prorate first payment / subscription length" msgid "For All Subscription Products" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:700 +#: includes/class-wc-subscriptions-switcher.php:715 #. translators: 1$: old item name, 2$: new item name when switching msgctxt "used in order notes" msgid "Customer switched from: %1$s to %2$s." msgstr "" -#: includes/class-wc-subscriptions-switcher.php:1594 +#: includes/class-wc-subscriptions-switcher.php:1604 msgctxt "a switch order" msgid "Downgrade" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:1597 +#: includes/class-wc-subscriptions-switcher.php:1607 msgctxt "a switch order" msgid "Upgrade" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:1600 +#: includes/class-wc-subscriptions-switcher.php:1610 msgctxt "a switch order" msgid "Crossgrade" msgstr "" -#: includes/class-wc-subscriptions-switcher.php:1605 +#: includes/class-wc-subscriptions-switcher.php:1615 #. translators: %1: product subtotal, %2: HTML span tag, %3: direction #. (upgrade, downgrade, crossgrade), %4: closing HTML span tag msgctxt "product subtotal string" @@ -3436,7 +3454,7 @@ msgctxt "input field placeholder for day field for annual subscriptions" msgid "Day" msgstr "" -#: includes/class-wcs-cart-renewal.php:563 +#: includes/class-wcs-cart-renewal.php:569 msgctxt "" "Used in WooCommerce by removed item notification: \"_All linked " "subscription items were_ removed. Undo?\" Filter for item title." @@ -3711,12 +3729,12 @@ msgctxt "text on submit button" msgid "Update Database" msgstr "" -#: includes/wcs-cart-functions.php:179 +#: includes/wcs-cart-functions.php:180 msgctxt "shipping method price" msgid "Free" msgstr "" -#: includes/wcs-cart-functions.php:254 +#: includes/wcs-cart-functions.php:255 #: templates/myaccount/view-subscription.php:199 #: templates/myaccount/view-subscription.php:204 #. translators: placeholder is price string, denotes tax included in cart/order diff --git a/woocommerce-subscriptions.php b/woocommerce-subscriptions.php index 6a1c857..0365279 100644 --- a/woocommerce-subscriptions.php +++ b/woocommerce-subscriptions.php @@ -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;