This commit is contained in:
Prospress Inc
2020-10-01 14:32:58 +02:00
committed by Remco Tolsma
parent 8faa5e8577
commit 420ccb2e64
404 changed files with 8838 additions and 7818 deletions

0
assets/css/about.css Executable file → Normal file
View File

0
assets/css/admin-order-statuses.css Executable file → Normal file
View File

3
assets/css/admin.css Executable file → Normal file
View File

@@ -271,7 +271,8 @@ a.close-subscriptions-search {
/* Add/Edit Subscription Screen */
#woocommerce-subscription-data .handlediv,
#woocommerce-subscription-data h2.hndle,
#woocommerce-subscription-data h3.hndle {
#woocommerce-subscription-data h3.hndle,
#woocommerce-subscription-data .postbox-header {
display: none;
}
#woocommerce-subscription-data .inside {

0
assets/css/checkout.css Executable file → Normal file
View File

0
assets/css/dashboard.css Executable file → Normal file
View File

0
assets/css/modal.css Executable file → Normal file
View File

0
assets/css/view-subscription.css Executable file → Normal file
View File

0
assets/css/wcs-upgrade.css Executable file → Normal file
View File

0
assets/images/add-edit-subscription-screen.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

0
assets/images/admin-change-payment-method.jpg Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

0
assets/images/ajax-loader.gif Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 885 B

After

Width:  |  Height:  |  Size: 885 B

0
assets/images/ajax-loader@2x.gif Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

0
assets/images/billing-schedules-meta-box.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

0
assets/images/checkout-recurring-totals.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

0
assets/images/drip-downloadable-content.jpg Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

0
assets/images/gift-subscription.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

0
assets/images/renewal-retry-settings.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

0
assets/images/subscribe-all-the-things.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

0
assets/images/subscription-reports.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

0
assets/images/subscription-suspended-email.jpg Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 172 KiB

0
assets/images/subscriptions-importer-exporter.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

0
assets/images/view-subscription.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 250 KiB

0
assets/images/woocommerce_subscriptions_logo.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

0
assets/js/admin/admin-pointers.js Executable file → Normal file
View File

9
assets/js/admin/admin.js Executable file → Normal file
View File

@@ -528,15 +528,6 @@ jQuery(document).ready(function($){
}
});
$(window).load(function(){
if($('[name="contains_subscription"]').length > 0 && $('[name="contains_subscription"]').val()=='true'){
// Show the Recurring Order Totals meta box in WC 2.2
$('#woocommerce-order-totals').show();
} else {
$('#woocommerce-order-totals').hide();
}
});
// Editing a variable product
$('#variable_product_options').on('change','[name^="variable_regular_price"]',function(){
var matches = $(this).attr('name').match(/\[(.*?)\]/);

0
assets/js/admin/jquery.flot.axislabels.js Executable file → Normal file
View File

0
assets/js/admin/jquery.flot.axislabels.min.js vendored Executable file → Normal file
View File

0
assets/js/admin/jquery.flot.orderBars.js Executable file → Normal file
View File

0
assets/js/admin/jquery.flot.orderBars.min.js vendored Executable file → Normal file
View File

0
assets/js/admin/jstz.js Executable file → Normal file
View File

0
assets/js/admin/jstz.min.js vendored Executable file → Normal file
View File

0
assets/js/admin/meta-boxes-coupon.js Executable file → Normal file
View File

0
assets/js/admin/meta-boxes-subscription.js Executable file → Normal file
View File

0
assets/js/admin/moment.js Executable file → Normal file
View File

0
assets/js/admin/moment.min.js vendored Executable file → Normal file
View File

0
assets/js/admin/reports.js Executable file → Normal file
View File

79
assets/js/admin/tracks.js Normal file
View File

@@ -0,0 +1,79 @@
jQuery( function( $ ) {
if ( ! window.hasOwnProperty( 'wcTracks' ) ) {
return;
}
function record_event( eventName, properties = {} ) {
window.wcTracks.recordEvent( eventName, properties );
}
// Add event listeners to Subscription Events by Date report clickable filters.
if ( $( "#report_subscription_events_by_date_new" ).length ) {
var filters = {
'new': 'subscriptions_report_events_by_date_new_filter_click',
'signups': 'subscriptions_report_events_by_date_signups_filter_click',
'resubscribes': 'subscriptions_report_events_by_date_resubscribes_filter_click',
'renewals': 'subscriptions_report_events_by_date_renewals_filter_click',
'switches': 'subscriptions_report_events_by_date_switches_filter_click',
'cancellations': 'subscriptions_report_events_by_date_cancellations_filter_click',
'ended': 'subscriptions_report_events_by_date_ended_filter_click',
'current': 'subscriptions_report_events_by_date_current_filter_click',
}
$.each( filters, function( key, value ) {
$( "#report_subscription_events_by_date_" + key ).on( 'click', function() {
// if range is not a URL param, we are looking at the default 7 day range.
var properties = {
range: location.search.includes( 'range' ) ? location.search.match( /range=([^&#]*)/ )[1] : '7day'
};
if ( 'custom' === properties.range ) {
// Start or end dates may be ommitted.
properties.start_date = location.search.includes( 'start_date=' )
? location.search.match( /start_date=([^&#]*)/ )[1]
: null;
properties.end_date = location.search.includes( 'end_date=' )
? location.search.match( /end_date=([^&#]*)/ )[1]
: new Date().toISOString().split( 'T' )[0];
properties.span = properties.start_date
? Math.floor( ( new Date( properties.end_date ) - new Date( properties.start_date ) ) / 86400000 ) + 1 + 'day'
: null;
}
record_event( value, properties );
} );
} );
}
// Add event listeners to Subscription by Product report links.
if ( $( "tbody[ data-wp-lists='list:product' ]" ).length ) {
$( "td.product_name a" ).on( 'click', function() {
record_event( 'subscriptions_report_by_product_name_click' );
} );
$( "td.subscription_count a" ).on( 'click', function() {
record_event( 'subscriptions_report_by_product_count_click' );
} );
}
// Add event listeners to Subscription by Customer report links.
if ( $( "tbody[ data-wp-lists='list:customer' ]" ).length ) {
$( "td.customer_name a" ).on( 'click', function() {
record_event( 'subscriptions_report_by_customer_name_click' );
} );
$( "td.total_subscription_count a" ).on( 'click', function() {
record_event( 'subscriptions_report_by_customer_total_count_click' );
} );
$( "td.total_subscription_order_count a" ).on( 'click', function() {
record_event( 'subscriptions_report_by_customer_total_order_count_click' );
} );
}
});

0
assets/js/admin/wcs-meta-boxes-order.js Executable file → Normal file
View File

0
assets/js/frontend/single-product.js Executable file → Normal file
View File

0
assets/js/frontend/view-subscription.js Executable file → Normal file
View File

0
assets/js/modal.js Executable file → Normal file
View File

0
assets/js/wcs-upgrade.js Executable file → Normal file
View File

79
changelog.txt Executable file → Normal file
View File

@@ -1,5 +1,84 @@
*** WooCommerce Subscriptions Changelog ***
2020-09-29 - version 3.0.9
* Fix: Offset subscription next payment date calculations based on site time. All dates still remain in UTC, but calculations on the 1st and last day of the month will now take into account the site timezone. PR#3708
* Fix: Only attempt to display the customer notice when updating the default token from the My Account page. PR#3799
* Fix: Prefix variation attributes with `attribute_` when setting up renewal carts. PR#3809
* Dev: Add a new hook to the change_users_subscription() function - this hook is triggered before the status change. PR#3787
2020-09-24 - version 3.0.8
* Tweak: Remove the '$X now' from price string when synced product sign up date is today. PR#3797
* Fix: Treat a subscription with a cancelled parent order as "needing payment". This ensures cancelling the subscription will go right to cancelled, rather than pending-cancellation. PR#3784
* Fix: Remove code responsible for hiding/showing the WC order totals meta box. This fixes the deprecated jQuery.fn.load() warnings. PR#3786
* Fix: Fix updating status and end dates via the REST API. PR#3796
* Dev: Add an action hook to edit subscription admin payment meta inputs. Enables third-parties to display custom fields for each gateway. PR#3798
* Dev: Opt-in tracking data for Subscriptions Reports PR#3738
Data sent to WooCommerce:
Subscriptions report views
Subscriptions report filter usage - clicks and report range
Number of subscriptions, orders, products
Plugin version
To disable this tracking, opt-out of WooCommerce tracking, see https://woocommerce.com/usage-tracking/
2020-08-11 - version 3.0.7
* Fix: Prevent possible errors caused by inaccessible get_current_screen() function. PR#3678
* Fix: Decouple single_add_to_cart_text() from add_to_cart_text() and remove double and incorrect filtering. PR#3705
* Fix: Account for the pre-paid term when canceling a subscription which is on-hold without a required payment. PR#3726
* Fix: [WC4.4] Replace uses of `get_product_from_item()` with `$item->get_product()`. PR#3769
* Fix: [WC4.4] Deprecate use of the WC_Cart::tax_display_cart. PR#3741
* Fix: [WC4.4] Update WC_Product_Variable_Subscription::get_available_variations() function signature. PR#3761
* Fix: [WC4.0] Use the WC version appropriate hook to override the text explaining uneditable subscriptions. PR#3678
* Fix: [WP5.5] Add permission callback for the REST statuses endpoint. PR#3765
* Fix: [WP5.5] Remove the post box header from edit subscription meta boxes #3771
* Performance: Validate product type changes on large sites to avoid running queries on edit product page loads. PR#3699
* Tweak: [WC Code Standards] Fixes for i18n-related issues. PR#3757
2020-07-29 - version 3.0.6
* Tweak: Add placeholder explanation for the upcoming recurring report. PR#3655
* Tweak: Add a link to QuickForget.com in the IPN error notice. PR#3744
* Fix: Fix COD gateway availability for trial subscriptions. PR#3740
* Fix: Fetch customer error notice messages on the change payment method page in WC post 3.9 version compatible way. PR#3730
* Fix: Don't reduce stock for items added to a subscription manually via admin screen. PR#3725
* Fix: Only display the automatic renewal note in emails when there's a next payment. PR#3679
* Fix: Use the interval to calculate first payment date for synced products. Fixes an issue that caused a purchase outside the sync grace period to not account for the subscription interval. PR#3303
* Dev: Add two new action hooks before and after change payment submit button. PR#3718
* Dev: Add a new helper function wcs_trial_has_passed(). PR#3289
* Dev: Replace uses of cal_days_in_month() with a date() equivalent. PR#3734
* Dev: Make various changes to improve coding standards. PRs#3748, #3749, #3753
2020-06-16 - version 3.0.5
* Fix: Ensure the mixed checkout setting correctly prevents mixed carts after customer login. PR#3696
* Fix: Translate the automatic subscription recurring note in emails. PR#3684
* Fix: Account for limited coupon use across recurring carts. Fixes an issue which leads to limited coupons applying beyond the limit. PR#3552
* Fix: Copy the item usage limit to coupons applied to manual renewal carts. PR#3686
* Dev: Add a filter to control access to the early renewal modal. PR#3675
2020-04-28 - version 3.0.4
* Fix: Save changes to payment method meta on the edit subscription screen. PR#3666
* Fix: Grant Shop Managers edit capabilities over subscriber users. PR#3669
* Fix: Remove original item from switched subscription during a multiple line item, different billing schedule switch. PR#3659
* Fix: Use strict conditionals when comparing results of as_next_scheduled_action. Fixes rescheduling of actions which were ran manually. PR#3661
* Tweak: To comply with Visa's changes to rules governing free trial subscriptions, add additional subscription details to email templates. PR#3664
2020-04-01 - version 3.0.3
* Fix: Only set the payment method if it has changed via admin edit. PR#3646
* Fix: Performance issue in WCS_Payment_Tokens::get_subscriptions_from_token. PR#3649
* Fix: Exclude empty nonce.
2020-03-11 - version 3.0.2
* New: Add WC Admin Compatibility. PR#3628
* Fix: Restore the trial end date after reactivating a subscription which was cancelled. PR#3572
* Fix: Hide the order again button for orders which contain limited products. PR#3612
* Fix: Don't show recurring cart taxes when no taxes apply. PR#3633
* Fix: Prevent autocompleting orders which contain free, simple, non-digital products. PR#3621
* Fix: Remove redundant double line from customer report query. PR#3640
* Fix: [PayPal] Modify WC payload regex to account for alphanumeric chars. PR#3629
* Fix: Hide the free shipping label for free shipping and free local pickup options. PR#3618
* Fix: Prevent fatal errors when getting price for variable products which aren't synced. PR#3616
* Fix: Don't send WC core emails for subscription related orders when in Staging mode. PR#3576
* Fix: Update PayPal settings URL used in admin notices. PR#3617
* Tweak: Update wording in warning when attempting to change the subscription product type. PR#3623
2020.01.17 - version 3.0.1
* Fix: Support multiple modals on the customer's view subscription page. Fixes an issue where preventing the early renewal modal being displayed was preventing all modals on the page from being displayed. PR#3571
* Fix: Silence PHP warning in events by date report caused by empty set of data. PR#3577

View File

0
includes/abstracts/abstract-wcs-background-updater.php Executable file → Normal file
View File

View File

0
includes/abstracts/abstract-wcs-cache-manager.php Executable file → Normal file
View File

0
includes/abstracts/abstract-wcs-customer-store.php Executable file → Normal file
View File

View File

0
includes/abstracts/abstract-wcs-debug-tool.php Executable file → Normal file
View File

View File

0
includes/abstracts/abstract-wcs-hook-deprecator.php Executable file → Normal file
View File

0
includes/abstracts/abstract-wcs-migrator.php Executable file → Normal file
View File

View File

@@ -144,7 +144,8 @@ abstract class WCS_Related_Order_Store {
*/
protected function check_relation_type( $relation_type ) {
if ( ! isset( self::$relation_type_keys[ $relation_type ] ) ) {
throw new InvalidArgumentException( sprintf( __( 'Invalid relation type: %s. Order relationship type must be one of: %s.', 'woocommerce-subscriptions' ), $relation_type, implode( ', ', $this->get_relation_types() ) ) );
// translators: 1: relation type, 2: list of valid relation types.
throw new InvalidArgumentException( sprintf( __( 'Invalid relation type: %1$s. Order relationship type must be one of: %2$s.', 'woocommerce-subscriptions' ), $relation_type, implode( ', ', $this->get_relation_types() ) ) );
}
}
}

0
includes/abstracts/abstract-wcs-retry-store.php Executable file → Normal file
View File

0
includes/abstracts/abstract-wcs-scheduler.php Executable file → Normal file
View File

0
includes/abstracts/abstract-wcs-table-maker.php Executable file → Normal file
View File

156
includes/admin/class-wc-subscriptions-admin.php Executable file → Normal file
View File

@@ -130,13 +130,18 @@ class WC_Subscriptions_Admin {
add_action( 'woocommerce_payment_gateways_settings', __CLASS__ . '::add_recurring_payment_gateway_information', 10, 1 );
// Change text for when order items cannot be edited
add_action( 'woocommerce_admin_order_totals_after_refunded', __CLASS__ . '::maybe_attach_gettext_callback', 10, 1 );
wcs_add_woocommerce_dependent_action( 'woocommerce_admin_order_totals_after_total', array( __CLASS__, 'maybe_attach_gettext_callback' ), '4.0.0', '>' );
wcs_add_woocommerce_dependent_action( 'woocommerce_admin_order_totals_after_refunded', array( __CLASS__, 'maybe_attach_gettext_callback' ), '4.0.0', '<' );
// Unhook gettext callback to prevent extra call impact
add_action( 'woocommerce_order_item_add_action_buttons', __CLASS__ . '::maybe_unattach_gettext_callback', 10, 1 );
// Add a reminder on the enable guest checkout setting that subscriptions still require an account
add_filter( 'woocommerce_payment_gateways_settings', array( __CLASS__, 'add_guest_checkout_setting_note' ), 10, 1 );
add_filter( 'woocommerce_account_settings', array( __CLASS__, 'add_guest_checkout_setting_note' ), 10, 1 );
// Validate the product type change before other product changes are saved.
add_action( 'woocommerce_process_product_meta', array( __CLASS__, 'validate_product_type_change' ), 5 );
}
/**
@@ -290,7 +295,12 @@ class WC_Subscriptions_Admin {
// Subscription Price, Interval and Period
?><p class="form-field _subscription_price_fields _subscription_price_field">
<label for="_subscription_price"><?php printf( esc_html__( 'Subscription price (%s)', 'woocommerce-subscriptions' ), esc_html( get_woocommerce_currency_symbol() ) ); ?></label>
<label for="_subscription_price">
<?php
// translators: %s: currency symbol.
printf( esc_html__( 'Subscription price (%s)', 'woocommerce-subscriptions' ), esc_html( get_woocommerce_currency_symbol() ) );
?>
</label>
<span class="wrap">
<input type="text" id="_subscription_price" name="_subscription_price" class="wc_input_price wc_input_subscription_price" placeholder="<?php echo esc_attr_x( 'e.g. 5.90', 'example price', 'woocommerce-subscriptions' ); ?>" step="any" min="0" value="<?php echo esc_attr( wc_format_localized_price( $chosen_price ) ); ?>" />
<label for="_subscription_period_interval" class="wcs_hidden_label"><?php esc_html_e( 'Subscription interval', 'woocommerce-subscriptions' ); ?></label>
@@ -310,7 +320,8 @@ class WC_Subscriptions_Admin {
</p><?php
// Subscription Length
woocommerce_wp_select( array(
woocommerce_wp_select(
array(
'id' => '_subscription_length',
'class' => 'wc_input_subscription_length select short',
'label' => __( 'Expire after', 'woocommerce-subscriptions' ),
@@ -763,6 +774,7 @@ class WC_Subscriptions_Admin {
$user = new WP_User( absint( $_POST['customer_user'] ) );
if ( 0 === $user->ID ) {
// translators: %s: subscription status.
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 ) );
}
}
@@ -823,8 +835,8 @@ class WC_Subscriptions_Admin {
'bulkEditIntervalhMessage' => __( 'Enter a new interval as a single number (e.g. to charge every 2nd month, enter 2):', 'woocommerce-subscriptions' ),
'bulkDeleteOptionLabel' => __( 'Delete all variations without a subscription', 'woocommerce-subscriptions' ),
'oneTimeShippingCheckNonce' => wp_create_nonce( 'one_time_shipping' ),
'productHasSubscriptions' => wcs_get_subscriptions_for_product( $post->ID, 'ids', array( 'limit' => 1 ) ) ? 'yes' : 'no',
'productTypeWarning' => __( 'Product type can not be changed because this product is associated with active subscriptions', 'woocommerce-subscriptions' ),
'productHasSubscriptions' => ! wcs_is_large_site() && wcs_get_subscriptions_for_product( $post->ID, 'ids', array( 'limit' => 1 ) ) ? 'yes' : 'no',
'productTypeWarning' => self::get_change_product_type_warning(),
);
} elseif ( 'edit-shop_order' == $screen->id ) {
$script_params = array(
@@ -890,7 +902,13 @@ class WC_Subscriptions_Admin {
$woocommerce_plugin_dir_file = self::get_woocommerce_plugin_dir_file();
// check if subscription products exist in the store
$subscription_product = wc_get_products( array( 'type' => array( 'subscription', 'variable-subscription' ), 'limit' => 1, 'return' => 'ids' ) );
$subscription_product = wc_get_products(
array(
'type' => array( 'subscription', 'variable-subscription' ),
'limit' => 1,
'return' => 'ids',
)
);
if ( ! empty( $woocommerce_plugin_dir_file ) && 0 == count( $subscription_product ) ) {
@@ -988,7 +1006,9 @@ class WC_Subscriptions_Admin {
* @since 1.3.1
*/
public static function add_manage_subscriptions_screen_options() {
add_screen_option( 'per_page', array(
add_screen_option(
'per_page',
array(
'label' => __( 'Subscriptions', 'woocommerce-subscriptions' ),
'default' => 10,
'option' => self::$option_prefix . '_admin_per_page',
@@ -1059,7 +1079,7 @@ class WC_Subscriptions_Admin {
$settings = self::get_settings();
$defaults_to_find = array(
self::$option_prefix . '_add_to_cart_button_text' => '',
self::$option_prefix . '_order_button_text' => '',
self::$option_prefix . '_order_button_text' => '', // phpcs:ignore WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned
);
// Add the $_POST[ 'woocommerce_subscriptions_allow_switching' ] value
@@ -1211,13 +1231,16 @@ class WC_Subscriptions_Admin {
'placeholder' => __( 'Sign up now', 'woocommerce-subscriptions' ),
),
array( 'type' => 'sectionend', 'id' => self::$option_prefix . '_button_text' ),
array(
'type' => 'sectionend',
'id' => self::$option_prefix . '_button_text',
),
array(
'name' => __( 'Roles', 'woocommerce-subscriptions' ),
'type' => 'title',
// translators: placeholders are <em> tags
'desc' => sprintf( __( 'Choose the default roles to assign to active and inactive subscribers. For record keeping purposes, a user account must be created for subscribers. Users with the %sadministrator%s role, such as yourself, will never be allocated these roles to prevent locking out administrators.', 'woocommerce-subscriptions' ), '<em>', '</em>' ),
'desc' => sprintf( __( 'Choose the default roles to assign to active and inactive subscribers. For record keeping purposes, a user account must be created for subscribers. Users with the %1$sadministrator%2$s role, such as yourself, will never be allocated these roles to prevent locking out administrators.', 'woocommerce-subscriptions' ), '<em>', '</em>' ),
'id' => self::$option_prefix . '_role_options',
),
@@ -1245,7 +1268,10 @@ class WC_Subscriptions_Admin {
'desc_tip' => true,
),
array( 'type' => 'sectionend', 'id' => self::$option_prefix . '_role_options' ),
array(
'type' => 'sectionend',
'id' => self::$option_prefix . '_role_options',
),
array(
'name' => _x( 'Renewals', 'option section heading', 'woocommerce-subscriptions' ),
@@ -1261,7 +1287,7 @@ class WC_Subscriptions_Admin {
'default' => 'no',
'type' => 'checkbox',
// translators: placeholders are opening and closing link tags
'desc_tip' => sprintf( __( 'With manual renewals, a customer\'s subscription is put on-hold until they login and pay to renew it. %sLearn more%s.', 'woocommerce-subscriptions' ), '<a href="http://docs.woocommerce.com/document/subscriptions/store-manager-guide/#accept-manual-renewals">', '</a>' ),
'desc_tip' => sprintf( __( 'With manual renewals, a customer\'s subscription is put on-hold until they login and pay to renew it. %1$sLearn more%2$s.', 'woocommerce-subscriptions' ), '<a href="http://docs.woocommerce.com/document/subscriptions/store-manager-guide/#accept-manual-renewals">', '</a>' ),
'checkboxgroup' => 'start',
'show_if_checked' => 'option',
),
@@ -1272,12 +1298,15 @@ class WC_Subscriptions_Admin {
'default' => 'no',
'type' => 'checkbox',
// translators: placeholders are opening and closing link tags
'desc_tip' => sprintf( __( 'If you don\'t want new subscription purchases to automatically charge renewal payments, you can turn off automatic payments. Existing automatic subscriptions will continue to charge customers automatically. %sLearn more%s.', 'woocommerce-subscriptions' ), '<a href="http://docs.woocommerce.com/document/subscriptions/store-manager-guide/#turn-off-automatic-payments">', '</a>' ),
'desc_tip' => sprintf( __( 'If you don\'t want new subscription purchases to automatically charge renewal payments, you can turn off automatic payments. Existing automatic subscriptions will continue to charge customers automatically. %1$sLearn more%2$s.', 'woocommerce-subscriptions' ), '<a href="http://docs.woocommerce.com/document/subscriptions/store-manager-guide/#turn-off-automatic-payments">', '</a>' ),
'checkboxgroup' => 'end',
'show_if_checked' => 'yes',
),
array( 'type' => 'sectionend', 'id' => self::$option_prefix . '_renewal_options' ),
array(
'type' => 'sectionend',
'id' => self::$option_prefix . '_renewal_options',
),
array(
'name' => _x( 'Miscellaneous', 'options section heading', 'woocommerce-subscriptions' ),
@@ -1321,10 +1350,14 @@ class WC_Subscriptions_Admin {
'id' => self::$option_prefix . '_drip_downloadable_content_on_renewal',
'default' => 'no',
'type' => 'checkbox',
// translators: %s is a line break.
'desc_tip' => sprintf( __( 'Enabling this grants access to new downloadable files added to a product only after the next renewal is processed.%sBy default, access to new downloadable files added to a product is granted immediately to any customer that has an active subscription with that product.', 'woocommerce-subscriptions' ), '<br />' ),
),
array( 'type' => 'sectionend', 'id' => self::$option_prefix . '_miscellaneous' ),
array(
'type' => 'sectionend',
'id' => self::$option_prefix . '_miscellaneous',
),
) );
}
@@ -1356,8 +1389,23 @@ class WC_Subscriptions_Admin {
<div class="squeezer">
<h4>
<?php
echo wp_kses(
sprintf(
// translators: $1-$2: opening and closing <strong> tags, $3-$4: opening and closing <em> tags
echo wp_kses( sprintf( __( '%1$sWooCommerce Subscriptions Installed%2$s &#8211; %3$sYou\'re ready to start selling subscriptions!%4$s', 'woocommerce-subscriptions' ), '<strong>', '</strong>', '<em>', '</em>' ), array( 'strong' => true, 'em' => true ) );
__(
'%1$sWooCommerce Subscriptions Installed%2$s &#8211; %3$sYou\'re ready to start selling subscriptions!%4$s',
'woocommerce-subscriptions'
),
'<strong>',
'</strong>',
'<em>',
'</em>'
),
array(
'strong' => true,
'em' => true,
)
);
?>
</h4>
@@ -1490,7 +1538,7 @@ class WC_Subscriptions_Admin {
$admin_notice = new WCS_Admin_Notice( 'error' );
$admin_notice->set_simple_content( sprintf(
/* translators: Placeholders are opening and closing link tags. */
__( "We weren't able to locate the set of report results you requested. Please regenerate the link from the %sSubscription Reports screen%s.", 'woocommerce-subscriptions' ),
__( 'We weren\'t able to locate the set of report results you requested. Please regenerate the link from the %1$sSubscription Reports screen%2$s.', 'woocommerce-subscriptions' ),
'<a href="' . esc_url( admin_url( 'admin.php?page=wc-reports&tab=subscriptions&report=subscription_events_by_date' ) ) . '">',
'</a>'
) );
@@ -1576,12 +1624,12 @@ class WC_Subscriptions_Admin {
esc_url( remove_query_arg( $query_arg ) )
);
// translators: placeholders are opening link tag, ID of sub, and closing link tag
printf( esc_html__( 'Showing orders for %sSubscription %s%s', 'woocommerce-subscriptions' ), '<a href="' . esc_url( get_edit_post_link( absint( $_GET[ $query_arg ] ) ) ) . '">', esc_html( $initial_order->get_order_number() ), '</a>' );
printf( esc_html__( 'Showing orders for %1$sSubscription %2$s%3$s', 'woocommerce-subscriptions' ), '<a href="' . esc_url( get_edit_post_link( absint( $_GET[ $query_arg ] ) ) ) . '">', esc_html( $initial_order->get_order_number() ), '</a>' );
echo '</p>';
} else {
echo '<div class="updated dismiss-subscriptions-search"><p>';
// translators: placeholders are opening link tag, ID of sub, and closing link tag
printf( esc_html__( 'Showing orders for %sSubscription %s%s', 'woocommerce-subscriptions' ), '<a href="' . esc_url( get_edit_post_link( absint( $_GET[ $query_arg ] ) ) ) . '">', esc_html( $initial_order->get_order_number() ), '</a>' );
printf( esc_html__( 'Showing orders for %1$sSubscription %2$s%3$s', 'woocommerce-subscriptions' ), '<a href="' . esc_url( get_edit_post_link( absint( $_GET[ $query_arg ] ) ) ) . '">', esc_html( $initial_order->get_order_number() ), '</a>' );
echo '</p>';
printf(
'<a href="%1$s" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></a>',
@@ -1605,7 +1653,7 @@ class WC_Subscriptions_Admin {
if ( 'combined' == $form ) {
// translators: number of 1$: days, 2$: weeks, 3$: months, 4$: years
$error_message = sprintf( __( 'The trial period can not exceed: %1s, %2s, %3s or %4s.', 'woocommerce-subscriptions' ), array_pop( $subscription_ranges['day'] ), array_pop( $subscription_ranges['week'] ), array_pop( $subscription_ranges['month'] ), array_pop( $subscription_ranges['year'] ) );
$error_message = sprintf( __( 'The trial period can not exceed: %1$s, %2$s, %3$s or %4$s.', 'woocommerce-subscriptions' ), array_pop( $subscription_ranges['day'] ), array_pop( $subscription_ranges['week'] ), array_pop( $subscription_ranges['month'] ), array_pop( $subscription_ranges['year'] ) );
} else {
$error_message = array();
foreach ( wcs_get_available_time_periods() as $period => $string ) {
@@ -1709,8 +1757,8 @@ class WC_Subscriptions_Admin {
* @return array
*/
public static function payment_gateways_renewal_column( $header ) {
$header_new = array_slice( $header, 0, count( $header ) - 1, true ) + array( 'renewals' => __( 'Automatic Recurring Payments', 'woocommerce-subscriptions' ) ) + // Ideally, we could add a link to the docs here, but the title is passed through esc_html()
array_slice( $header, count( $header ) - 1, count( $header ) - ( count( $header ) - 1 ), true );
$header_new = array_slice( $header, 0, count( $header ) - 1, true ) + array( 'renewals' => __( 'Automatic Recurring Payments', 'woocommerce-subscriptions' ) ) // Ideally, we could add a link to the docs here, but the title is passed through esc_html()
+ array_slice( $header, count( $header ) - 1, count( $header ) - ( count( $header ) - 1 ), true );
return $header_new;
}
@@ -1802,10 +1850,12 @@ class WC_Subscriptions_Admin {
*/
public static function maybe_attach_gettext_callback() {
if ( is_admin() && function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
if ( is_object( $screen ) && 'shop_subscription' == $screen->id ) {
add_filter( 'gettext', __CLASS__ . '::change_order_item_editable_text', 10, 3 );
if ( is_object( $screen ) && 'shop_subscription' === $screen->id ) {
add_filter( 'gettext', array( __CLASS__, 'change_order_item_editable_text' ), 10, 3 );
}
}
}
@@ -1816,10 +1866,12 @@ class WC_Subscriptions_Admin {
*/
public static function maybe_unattach_gettext_callback() {
if ( is_admin() && function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
if ( is_object( $screen ) && 'shop_subscription' == $screen->id ) {
remove_filter( 'gettext', __CLASS__ . '::change_order_item_editable_text', 10 );
if ( is_object( $screen ) && 'shop_subscription' === $screen->id ) {
remove_filter( 'gettext', array( __CLASS__, 'change_order_item_editable_text' ), 10 );
}
}
}
@@ -1833,7 +1885,6 @@ class WC_Subscriptions_Admin {
public static function change_order_item_editable_text( $translated_text, $text, $domain ) {
switch ( $text ) {
case 'This order is no longer editable.':
$translated_text = __( 'Subscription items can no longer be edited.', 'woocommerce-subscriptions' );
break;
@@ -1871,7 +1922,7 @@ class WC_Subscriptions_Admin {
array(
// translators: placeholders are opening and closing link tags
'desc' => sprintf( __( 'Payment gateways which don\'t support automatic recurring payments can be used to process %smanual subscription renewal payments%s.', 'woocommerce-subscriptions' ), '<a href="http://docs.woocommerce.com/document/subscriptions/renewal-process/">', '</a>' ),
'desc' => sprintf( __( 'Payment gateways which don\'t support automatic recurring payments can be used to process %1$smanual subscription renewal payments%2$s.', 'woocommerce-subscriptions' ), '<a href="http://docs.woocommerce.com/document/subscriptions/renewal-process/">', '</a>' ),
'id' => WC_Subscriptions_Admin::$option_prefix . '_payment_gateways_additional',
'type' => 'informational',
),
@@ -1884,10 +1935,12 @@ class WC_Subscriptions_Admin {
),
);
$insert_index = array_search( array(
$insert_index = array_search(
array(
'type' => 'sectionend',
'id' => 'payment_gateways_options',
), $settings
),
$settings
);
// reconstruct the settings array, inserting the new settings after the payment gatways table
@@ -1987,6 +2040,49 @@ class WC_Subscriptions_Admin {
return $settings;
}
/**
* Gets the product type warning message displayed for products associated with subscriptions
*
* @since 3.0.7
* @return string The change product type warning message.
*/
private static function get_change_product_type_warning() {
return __( 'The product type can not be changed because this product is associated with subscriptions.', 'woocommerce-subscriptions' );
}
/**
* Validates the product type change before other product data is saved.
*
* Subscription products associated with subscriptions cannot be changed. Doing so
* can cause issues. For example when customers who try to manually renew where the subscription
* products are placed in the cart.
*
* @since 3.0.7
* @param int $product_id The product ID being saved.
*/
public static function validate_product_type_change( $product_id ) {
if ( empty( $_POST['woocommerce_meta_nonce'] ) || ! wp_verify_nonce( wp_unslash( $_POST['woocommerce_meta_nonce'] ), 'woocommerce_save_data' ) || empty( $_POST['product-type'] ) ) {
return;
}
$current_product_type = WC_Product_Factory::get_product_type( $product_id );
// Only validate subscription product type changes.
if ( 'subscription' !== $current_product_type && 'variable-subscription' !== $current_product_type ) {
return;
}
$new_product_type = sanitize_title( wp_unslash( $_POST['product-type'] ) );
// Display an error and don't save the product if the type is changing and it's linked to subscriptions.
if ( $new_product_type !== $current_product_type && (bool) wcs_get_subscriptions_for_product( $product_id, 'ids', array( 'limit' => 1 ) ) ) {
wcs_add_admin_notice( self::get_change_product_type_warning(), 'error' );
wp_safe_redirect( get_admin_url( null, "post.php?post={$product_id}&action=edit" ) );
exit;
}
}
/**
* Renders the Subscription information in the WC status page
*/

39
includes/admin/class-wcs-admin-meta-boxes.php Executable file → Normal file
View File

@@ -37,17 +37,20 @@ class WCS_Admin_Meta_Boxes {
add_action( 'woocommerce_process_shop_order_meta', 'WCS_Meta_Box_Schedule::save', 10, 2 );
add_action( 'woocommerce_process_shop_order_meta', 'WCS_Meta_Box_Subscription_Data::save', 10, 2 );
add_filter( 'woocommerce_order_actions', __CLASS__ . '::add_subscription_actions', 10, 1 );
add_filter( 'woocommerce_order_actions', array( __CLASS__, 'add_subscription_actions' ), 10, 1 );
add_action( 'woocommerce_order_action_wcs_process_renewal', __CLASS__ . '::process_renewal_action_request', 10, 1 );
add_action( 'woocommerce_order_action_wcs_create_pending_renewal', __CLASS__ . '::create_pending_renewal_action_request', 10, 1 );
add_action( 'woocommerce_order_action_wcs_create_pending_parent', __CLASS__ . '::create_pending_parent_action_request', 10, 1 );
add_action( 'woocommerce_order_action_wcs_process_renewal', array( __CLASS__, 'process_renewal_action_request' ), 10, 1 );
add_action( 'woocommerce_order_action_wcs_create_pending_renewal', array( __CLASS__, 'create_pending_renewal_action_request' ), 10, 1 );
add_action( 'woocommerce_order_action_wcs_create_pending_parent', array( __CLASS__, 'create_pending_parent_action_request' ), 10, 1 );
if ( WC_Subscriptions::is_woocommerce_pre( '3.2' ) ) {
add_filter( 'woocommerce_resend_order_emails_available', __CLASS__ . '::remove_order_email_actions', 0, 1 );
add_filter( 'woocommerce_resend_order_emails_available', array( __CLASS__, 'remove_order_email_actions' ), 0, 1 );
}
add_action( 'woocommerce_order_action_wcs_retry_renewal_payment', __CLASS__ . '::process_retry_renewal_payment_action_request', 10, 1 );
add_action( 'woocommerce_order_action_wcs_retry_renewal_payment', array( __CLASS__, 'process_retry_renewal_payment_action_request' ), 10, 1 );
// Disable stock managment while adding line items to a subscription via AJAX.
add_action( 'option_woocommerce_manage_stock', array( __CLASS__, 'override_stock_management' ) );
}
/**
@@ -122,7 +125,10 @@ class WCS_Admin_Meta_Boxes {
wp_enqueue_script( 'wcs-admin-meta-boxes-order', plugin_dir_url( WC_Subscriptions::$plugin_file ) . 'assets/js/admin/wcs-meta-boxes-order.js' );
wp_localize_script( 'wcs-admin-meta-boxes-order', 'wcs_admin_order_meta_boxes', array(
wp_localize_script(
'wcs-admin-meta-boxes-order',
'wcs_admin_order_meta_boxes',
array(
'retry_renewal_payment_action_warning' => __( "Are you sure you want to retry payment for this renewal order?\n\nThis will attempt to charge the customer and send renewal order emails (if emails are enabled).", 'woocommerce-subscriptions' ),
)
);
@@ -231,6 +237,7 @@ class WCS_Admin_Meta_Boxes {
}
}
wc_maybe_reduce_stock_levels( $parent_order );
$subscription->add_order_note( __( 'Create pending parent order requested by admin action.', 'woocommerce-subscriptions' ), false, true );
}
@@ -301,4 +308,22 @@ class WCS_Admin_Meta_Boxes {
return $can_be_retried;
}
/**
* Disables stock managment while adding items to a subscription via the edit subscription screen.
*
* @since 3.0.6
*
* @param string $manage_stock The default manage stock setting.
* @return string Whether the stock should be managed.
*/
public static function override_stock_management( $manage_stock ) {
// Override stock management while adding line items to a subscription via AJAX.
if ( isset( $_POST['order_id'] ) && wp_verify_nonce( $_REQUEST['security'], 'order-item' ) && doing_action( 'wp_ajax_woocommerce_add_order_item' ) && wcs_is_subscription( absint( wp_unslash( $_POST['order_id'] ) ) ) ) {
$manage_stock = 'no';
}
return $manage_stock;
}
}

0
includes/admin/class-wcs-admin-notice.php Executable file → Normal file
View File

145
includes/admin/class-wcs-admin-post-types.php Executable file → Normal file
View File

@@ -578,15 +578,23 @@ class WCS_Admin_Post_Types {
break;
case 1:
foreach ( $subscription_items as $item ) {
$column_content .= self::get_item_display( $item, $the_subscription );
$item_name = wp_kses( self::get_item_name_html( $item, $item->get_product() ), array( 'a' => array( 'href' => array() ) ) );
$item_meta_html = self::get_item_meta_html( $item );
$meta_help_tip = $item_meta_html ? wcs_help_tip( $item_meta_html, true ) : '';
$column_content .= sprintf( '<div class="order-item">%s%s</div>', $item_name, $meta_help_tip );
}
break;
default:
// translators: %d: item count.
$column_content .= '<a href="#" class="show_order_items">' . esc_html( apply_filters( 'woocommerce_admin_order_item_count', sprintf( _n( '%d item', '%d items', $the_subscription->get_item_count(), 'woocommerce-subscriptions' ), $the_subscription->get_item_count() ), $the_subscription ) ) . '</a>';
$column_content .= '<table class="order_items" cellspacing="0">';
foreach ( $subscription_items as $item ) {
$column_content .= self::get_item_display( $item, $the_subscription, 'row' );
$item_name = self::get_item_name_html( $item, $item->get_product(), 'do_not_include_quantity' );
$item_meta_html = self::get_item_meta_html( $item );
$column_content .= self::get_item_display_row( $item, $item_name, $item_meta_html );
}
$column_content .= '</table>';
@@ -620,7 +628,7 @@ class WCS_Admin_Post_Types {
break;
}
echo wp_kses( apply_filters( 'woocommerce_subscription_list_table_column_content', $column_content, $the_subscription, $column ), array( 'a' => array( 'class' => array(), 'href' => array(), 'data-tip' => array(), 'title' => array() ), 'time' => array( 'class' => array(), 'title' => array() ), 'mark' => array( 'class' => array(), 'data-tip' => array() ), 'small' => array( 'class' => array() ), 'table' => array( 'class' => array(), 'cellspacing' => array(), 'cellpadding' => array() ), 'tr' => array( 'class' => array() ), 'td' => array( 'class' => array() ), 'div' => array( 'class' => array(), 'data-tip' => array() ), 'br' => array(), 'strong' => array(), 'span' => array( 'class' => array(), 'data-tip' => array() ), 'p' => array( 'class' => array() ), 'button' => array( 'type' => array(), 'class' => array() ) ) );
echo wp_kses( apply_filters( 'woocommerce_subscription_list_table_column_content', $column_content, $the_subscription, $column ), array( 'a' => array( 'class' => array(), 'href' => array(), 'data-tip' => array(), 'title' => array() ), 'time' => array( 'class' => array(), 'title' => array() ), 'mark' => array( 'class' => array(), 'data-tip' => array() ), 'small' => array( 'class' => array() ), 'table' => array( 'class' => array(), 'cellspacing' => array(), 'cellpadding' => array() ), 'tr' => array( 'class' => array() ), 'td' => array( 'class' => array() ), 'div' => array( 'class' => array(), 'data-tip' => array() ), 'br' => array(), 'strong' => array(), 'span' => array( 'class' => array(), 'data-tip' => array() ), 'p' => array( 'class' => array() ), 'button' => array( 'type' => array(), 'class' => array() ) ) ); // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
}
@@ -989,46 +997,24 @@ class WCS_Admin_Post_Types {
}
/**
* Get the HTML for an order item to display on the Subscription list table.
* Gets the HTML for a line item's meta to display on the Subscription list table.
*
* @param array $actions
* @param object $post
* @return array
* @param WC_Order_Item $item The line item object.
* @param mixed $deprecated
*
* @return string The line item meta html string generated by @see wc_display_item_meta().
*/
protected static function get_item_display( $item, $the_subscription, $element = 'div' ) {
$_product = apply_filters( 'woocommerce_order_item_product', $the_subscription->get_product_from_item( $item ), $item );
$item_meta_html = self::get_item_meta_html( $item, $_product );
if ( 'div' === $element ) {
$item_html = self::get_item_display_div( $item, self::get_item_name_html( $item, $_product ), $item_meta_html );
} else {
$item_html = self::get_item_display_row( $item, self::get_item_name_html( $item, $_product, 'do_not_include_quantity' ), $item_meta_html );
protected static function get_item_meta_html( $item, $deprecated = '' ) {
if ( $deprecated ) {
wcs_deprecated_argument( __METHOD__, '3.0.7', 'The second parameter (product) is no longer used.' );
}
return $item_html;
}
/**
* Get the HTML for order item meta to display on the Subscription list table.
*
* @param WC_Order_Item $item
* @param WC_Product $product
* @return string
*/
protected static function get_item_meta_html( $item, $_product ) {
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$item_meta = wcs_get_order_item_meta( $item, $_product );
$item_meta_html = $item_meta->display( true, true );
} else {
$item_meta_html = wc_display_item_meta( $item, array(
'before' => '',
'after' => '',
'separator' => '',
'echo' => false,
) );
}
return $item_meta_html;
}
@@ -1065,37 +1051,18 @@ class WCS_Admin_Post_Types {
}
/**
* Get the HTML for order item to display on the Subscription list table using a div element
* as the wrapper, which is done for subscriptions with a single line item.
* Gets the table row HTML content for a subscription line item.
*
* @param array $actions
* @param object $post
* @return array
*/
protected static function get_item_display_div( $item, $item_name, $item_meta_html ) {
$item_html = '<div class="order-item">';
$item_html .= wp_kses( $item_name, array( 'a' => array( 'href' => array() ) ) );
if ( $item_meta_html ) {
$item_html .= wcs_help_tip( $item_meta_html, true );
}
$item_html .= '</div>';
return $item_html;
}
/**
* Get the HTML for order item to display on the Subscription list table using a table element
* as the wrapper, which is done for subscriptions with multilpe line items.
* On the Subscriptions list table, subscriptions with multiple items display those line items in a table.
* This function generates an individual row for a specific line item.
*
* @param array $actions
* @param object $post
* @return array
* @param WC_Line_Item_Product $item The line item product object.
* @param string $item_name The line item's name.
* @param string $item_meta_html The line item's meta HTML generated by @see wc_display_item_meta().
*
* @return string The table row HTML content for a line item.
*/
protected static function get_item_display_row( $item, $item_name, $item_meta_html ) {
ob_start();
?>
<tr class="<?php echo esc_attr( apply_filters( 'woocommerce_admin_order_item_class', '', $item ) ); ?>">
@@ -1112,9 +1079,7 @@ class WCS_Admin_Post_Types {
</tr>
<?php
$item_html = ob_get_clean();
return $item_html;
return ob_get_clean();
}
/**
@@ -1151,4 +1116,58 @@ class WCS_Admin_Post_Types {
</select>
<?php
}
/** Deprecated Functions */
/**
* Get the HTML for an order item to display on the Subscription list table.
*
* @deprecated 3.0.7
*
* @param WC_Line_Item_Product $item The subscription line item object.
* @param WC_Subscription $subscription The subscription object. This variable is no longer used.
* @param string $element The type of element to generate. Can be 'div' or 'row'. Default is 'div'.
*
* @return string The line item column HTML content for a line item.
*/
protected static function get_item_display( $item, $subscription = '', $element = 'div' ) {
wcs_deprecated_function( __METHOD__, '3.0.7' );
$_product = $item->get_product();
$item_meta_html = self::get_item_meta_html( $item );
if ( 'div' === $element ) {
$item_html = self::get_item_display_div( $item, self::get_item_name_html( $item, $_product ), $item_meta_html );
} else {
$item_html = self::get_item_display_row( $item, self::get_item_name_html( $item, $_product, 'do_not_include_quantity' ), $item_meta_html );
}
return $item_html;
}
/**
* Gets the HTML for order item to display on the Subscription list table using a div element
* as the wrapper, which is done for subscriptions with a single line item.
*
* @deprecated 3.0.7
*
* @param WC_Line_Item_Product $item The line item object.
* @param string $item_name The line item's name.
* @param string $item_meta_html The line item's meta HTML.
*
* @return string The subcription line item column HTML content.
*/
protected static function get_item_display_div( $item, $item_name, $item_meta_html ) {
wcs_deprecated_function( '__METHOD__', '3.0.7' );
$item_html = '<div class="order-item">';
$item_html .= wp_kses( $item_name, array( 'a' => array( 'href' => array() ) ) );
if ( $item_meta_html ) {
$item_html .= wcs_help_tip( $item_meta_html, true );
}
$item_html .= '</div>';
return $item_html;
}
}

37
includes/admin/class-wcs-admin-reports.php Executable file → Normal file
View File

@@ -119,6 +119,11 @@ class WCS_Admin_Reports {
wp_enqueue_script( 'flot-order', plugin_dir_url( WC_Subscriptions::$plugin_file ) . 'assets/js/admin/jquery.flot.orderBars' . $suffix . '.js', array( 'jquery', 'flot' ), WC_Subscriptions::$version );
wp_enqueue_script( 'flot-axis-labels', plugin_dir_url( WC_Subscriptions::$plugin_file ) . 'assets/js/admin/jquery.flot.axislabels' . $suffix . '.js', array( 'jquery', 'flot' ), WC_Subscriptions::$version );
// Add tracks script if tracking is enabled.
if ( 'yes' === get_option( 'woocommerce_allow_tracking', 'no' ) ) {
wp_enqueue_script( 'wcs-tracks', plugin_dir_url( WC_Subscriptions::$plugin_file ) . 'assets/js/admin/tracks.js', array( 'jquery' ), WC_Subscriptions::$version, true );
}
}
}
@@ -142,7 +147,7 @@ class WCS_Admin_Reports {
/**
* Get a report from one of our classes.
*
* @param string $name
* @param string $name report name to be fetched.
*/
public static function get_report( $name ) {
$name = sanitize_title( str_replace( '_', '-', $name ) );
@@ -154,6 +159,36 @@ class WCS_Admin_Reports {
$report = new $class();
$report->output_report();
if ( class_exists( 'WC_Tracks' ) ) {
$reports = array(
'subscription-events-by-date' => 'subscriptions_report_events_by_date_view',
'upcoming-recurring-revenue' => 'subscriptions_report_upcoming_recurring_revenue_view',
'retention-rate' => 'subscriptions_report_retention_rate_view',
'subscription-by-product' => 'subscriptions_report_by_product_view',
'subscription-by-customer' => 'subscriptions_report_by_customer_view',
'subscription-payment-retry' => 'subscriptions_report_payment_retry_view',
);
$properties = array(
'orders_count' => array_sum( (array) wp_count_posts( 'shop_order' ) ),
'subscriptions_count' => array_sum( (array) wp_count_posts( 'shop_subscription' ) ),
'subscriptions_version' => WC_Subscriptions::$version,
);
if ( in_array( $name, array( 'subscription-events-by-date', 'upcoming-recurring-revenue', 'subscription-payment-retry' ), true ) ) {
$properties['range'] = ! empty( $_GET['range'] ) ? sanitize_text_field( $_GET['range'] ) : '7day'; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.NonceVerification.Recommended
if ( 'custom' === $properties['range'] ) {
// We have to get start date from _GET variables since $report sets this far into the past when empty.
$properties['start_date'] = ! empty( $_GET['start_date'] ) ? sanitize_text_field( $_GET['start_date'] ) : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.NonceVerification.Recommended
$properties['end_date'] = gmdate( 'Y-m-d', $report->end_date );
$properties['span'] = $properties['start_date'] ? floor( ( $report->end_date - $report->start_date ) / DAY_IN_SECONDS ) + 1 . 'day' : null;
}
}
WC_Tracks::record_event( $reports[ $name ], $properties );
}
}
/**

3
includes/admin/class-wcs-admin-system-status.php Executable file → Normal file
View File

@@ -141,7 +141,8 @@ class WCS_Admin_System_Status {
if ( ! empty( $theme_overrides['has_outdated_templates'] ) && true === $theme_overrides['has_outdated_templates'] ) {
$debug_data['wcs_theme_overrides'] += array(
'mark_icon' => 'warning',
'note' => sprintf( __( '%sLearn how to update%s', 'woocommerce-subscriptions' ), '<a href="https://docs.woocommerce.com/document/fix-outdated-templates-woocommerce/" target="_blank">', '</a>' ),
// translators: placeholders are opening/closing tags linking to documentation on outdated templates.
'note' => sprintf( __( '%1$sLearn how to update%2$s', 'woocommerce-subscriptions' ), '<a href="https://docs.woocommerce.com/document/fix-outdated-templates-woocommerce/" target="_blank">', '</a>' ),
);
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* WooCommerce Subscriptions WC Admin Manager.
*
* @author WooCommerce
* @category Admin
* @package WooCommerce Subscriptions/Admin
* @version 3.0.2
*/
defined( 'ABSPATH' ) || exit;
class WCS_WC_Admin_Manager {
/**
* Initialise the class and attach hook callbacks.
*/
public static function init() {
if ( ! defined( 'WC_ADMIN_PLUGIN_FILE' ) ) {
return;
}
add_action( 'admin_menu', array( __CLASS__, 'register_subscription_admin_pages' ) );
}
/**
* Connects existing WooCommerce Subscription admin pages to WooCommerce Admin.
*/
public static function register_subscription_admin_pages() {
// WooCommerce > Subscriptions.
wc_admin_connect_page(
array(
'id' => 'woocommerce-subscriptions',
'screen_id' => 'edit-shop_subscription',
'title' => __( 'Subscriptions', 'woocommerce-subscriptions' ),
'path' => add_query_arg( 'post_type', 'shop_subscription', 'edit.php' ),
)
);
// WooCommerce > Subscriptions > Add New.
wc_admin_connect_page(
array(
'id' => 'woocommerce-add-subscription',
'parent' => 'woocommerce-subscriptions',
'screen_id' => 'shop_subscription-add',
'title' => __( 'Add New', 'woocommerce-subscriptions' ),
)
);
// WooCommerce > Subscriptions > Edit Subscription.
wc_admin_connect_page(
array(
'id' => 'woocommerce-edit-subscription',
'parent' => 'woocommerce-subscriptions',
'screen_id' => 'shop_subscription',
'title' => __( 'Edit Subscription', 'woocommerce-subscriptions' ),
)
);
}
}

View File

View File

View File

View File

View File

View File

View File

@@ -109,7 +109,10 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
<p class="form-field form-field-wide">
<?php echo esc_html__( 'Parent order: ', 'woocommerce-subscriptions' ) ?>
<a href="<?php echo esc_url( get_edit_post_link( $subscription->get_parent_id() ) ); ?>">
<?php echo sprintf( esc_html__( '#%1$s', 'woocommerce-subscriptions' ), esc_html( $parent_order->get_order_number() ) ); ?>
<?php
// translators: placeholder is an order number.
echo sprintf( esc_html__( '#%1$s', 'woocommerce-subscriptions' ), esc_html( $parent_order->get_order_number() ) );
?>
</a>
</p>
<?php } else {
@@ -168,6 +171,7 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
// Display help tip
if ( '' != $subscription->get_payment_method() && ! $subscription->is_manual() ) {
// translators: %s: gateway ID.
echo wcs_help_tip( sprintf( _x( 'Gateway ID: [%s]', 'The gateway ID displayed on the Edit Subscriptions screen when editing payment method.', 'woocommerce-subscriptions' ), $subscription->get_payment_method() ) );
}
@@ -379,6 +383,7 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
wcs_delete_objects_property( $parent, 'subscription_renewal' );
}
$subscription->set_parent_id( wc_clean( $_POST['parent-order-id'] ) );
// translators: %s: parent order number (linked to its details screen).
$subscription->add_order_note( sprintf( _x( 'Subscription linked to parent order %s via admin.', 'subscription note after linking to a parent order', 'woocommerce-subscriptions' ), sprintf( '<a href="%1$s">#%2$s</a> ', esc_url( wcs_get_edit_post_link( $subscription->get_parent_id() ) ), $subscription->get_parent()->get_order_number() ) ), false, true );
$subscription->save();
}

View File

@@ -16,7 +16,10 @@ $order_post = wcs_get_objects_property( $order, 'post' );
<tr>
<td>
<a href="<?php echo esc_url( get_edit_post_link( wcs_get_objects_property( $order, 'id' ) ) ); ?>">
<?php echo sprintf( esc_html_x( '#%s', 'hash before order number', 'woocommerce-subscriptions' ), esc_html( $order->get_order_number() ) ); ?>
<?php
// translators: placeholder is an order number.
echo sprintf( esc_html_x( '#%s', 'hash before order number', 'woocommerce-subscriptions' ), esc_html( $order->get_order_number() ) );
?>
</a>
</td>
<td>
@@ -54,6 +57,6 @@ $order_post = wcs_get_objects_property( $order, 'post' );
?>
</td>
<td>
<span class="amount"><?php echo wp_kses( $order->get_formatted_order_total(), array( 'small' => array(), 'span' => array( 'class' => array() ), 'del' => array(), 'ins' => array() ) ); ?></span>
<span class="amount"><?php echo wp_kses( $order->get_formatted_order_total(), array( 'small' => array(), 'span' => array( 'class' => array() ), 'del' => array(), 'ins' => array() ) ); // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound ?></span>
</td>
</tr>

View File

0
includes/admin/meta-boxes/views/html-retries-table.php Executable file → Normal file
View File

View File

@@ -16,7 +16,8 @@ if ( ! defined( 'ABSPATH' ) ) {
<?php if ( $the_subscription->can_date_be_updated( 'next_payment' ) ) : ?>
<div class="billing-schedule-edit wcs-date-input"><?php
// Subscription Period Interval
echo woocommerce_wp_select( array(
echo woocommerce_wp_select(
array(
'id' => '_billing_interval',
'class' => 'billing_interval',
'label' => __( 'Payment:', 'woocommerce-subscriptions' ),
@@ -26,7 +27,8 @@ if ( ! defined( 'ABSPATH' ) ) {
);
// Billing Period
echo woocommerce_wp_select( array(
echo woocommerce_wp_select(
array(
'id' => '_billing_period',
'class' => 'billing_period',
'label' => __( 'Billing Period', 'woocommerce-subscriptions' ),
@@ -52,7 +54,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<strong><?php echo esc_html( $date_label ); ?>:</strong>
<input type="hidden" name="<?php echo esc_attr( $date_key ); ?>_timestamp_utc" id="<?php echo esc_attr( $date_key ); ?>_timestamp_utc" value="<?php echo esc_attr( $the_subscription->get_time( $internal_date_key, 'gmt' ) ); ?>"/>
<?php if ( $the_subscription->can_date_be_updated( $internal_date_key ) ) : ?>
<?php echo wp_kses( wcs_date_input( $the_subscription->get_time( $internal_date_key, 'site' ), array( 'name_attr' => $date_key ) ), array( 'input' => array( 'type' => array(), 'class' => array(), 'placeholder' => array(), 'name' => array(), 'id' => array(), 'maxlength' => array(), 'size' => array(), 'value' => array(), 'patten' => array() ), 'div' => array( 'class' => array() ), 'span' => array(), 'br' => array() ) ); ?>
<?php echo wp_kses( wcs_date_input( $the_subscription->get_time( $internal_date_key, 'site' ), array( 'name_attr' => $date_key ) ), array( 'input' => array( 'type' => array(), 'class' => array(), 'placeholder' => array(), 'name' => array(), 'id' => array(), 'maxlength' => array(), 'size' => array(), 'value' => array(), 'patten' => array() ), 'div' => array( 'class' => array() ), 'span' => array(), 'br' => array() ) ); // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound ?>
<?php else : ?>
<?php echo esc_html( $the_subscription->get_date_to_display( $internal_date_key ) ); ?>
<?php endif; ?>

View File

@@ -13,7 +13,10 @@ if ( ! defined( 'ABSPATH' ) ) {
?>
<tr>
<td>
<?php echo sprintf( esc_html_x( '#%s', 'hash before order number', 'woocommerce-subscriptions' ), esc_html( $order_id ) ); ?>
<?php
// translators: placeholder is an order ID.
echo sprintf( esc_html_x( '#%s', 'hash before order number', 'woocommerce-subscriptions' ), esc_html( $order_id ) );
?>
<div class="wcs-unknown-order-info-wrapper">
<a href="https://docs.woocommerce.com/document/subscriptions/orders/#section-8"><?php echo wcs_help_tip( sprintf( "This %s couldn't be loaded from the database. %s Click to learn more.", $relationship, '</br>' ) ); ?></a>
</div>

View File

@@ -106,6 +106,8 @@ class WCS_Report_Cache_Manager {
// Add system status information.
add_filter( 'wcs_system_status', array( $this, 'add_system_status_info' ) );
add_action( 'woocommerce_subscriptions_upgraded', array( $this, 'transfer_large_site_cache_option' ), 10, 2 );
}
/**
@@ -244,23 +246,7 @@ class WCS_Report_Cache_Manager {
protected function use_large_site_cache() {
if ( null === $this->use_large_site_cache ) {
if ( false == get_option( 'wcs_report_use_large_site_cache' ) ) {
$subscription_counts = (array) wp_count_posts( 'shop_subscription' );
$order_counts = (array) wp_count_posts( 'shop_order' );
if ( array_sum( $subscription_counts ) > 3000 || array_sum( $order_counts ) > 25000 ) {
update_option( 'wcs_report_use_large_site_cache', 'true', false );
$this->use_large_site_cache = true;
} else {
$this->use_large_site_cache = false;
}
} else {
$this->use_large_site_cache = true;
}
$this->use_large_site_cache = wcs_is_large_site();
}
return apply_filters( 'wcs_report_use_large_site_cache', $this->use_large_site_cache );
@@ -361,4 +347,24 @@ class WCS_Report_Cache_Manager {
return $cache_update_timestamp;
}
/**
* Transfers the 'wcs_report_use_large_site_cache' option to the new 'wcs_is_large_site' option.
*
* In 3.0.7 we introduced a more general use option, 'wcs_is_large_site', replacing the need for one specifically
* for report caching. This function migrates the existing option value if it was previously set.
*
* @since 3.0.7
*
* @param string $new_version The new Subscriptions plugin version.
* @param string $previous_version The version of Subscriptions prior to upgrade.
*/
public function transfer_large_site_cache_option( $new_version, $previous_version ) {
// Check if the plugin upgrade is from a version prior to the option being deprecated (before 3.0.7).
if ( version_compare( $previous_version, '3.0.7', '<' ) && false !== get_option( 'wcs_report_use_large_site_cache' ) ) {
update_option( 'wcs_is_large_site', 'yes', false );
delete_option( 'wcs_report_use_large_site_cache' );
}
}
}

10
includes/admin/reports/class-wcs-report-dashboard.php Executable file → Normal file
View File

@@ -215,7 +215,10 @@ class WCS_Report_Dashboard {
</li>
<li class="signup-revenue">
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-reports&tab=subscriptions&report=subscription_events_by_date&range=month' ) ); ?>">
<?php echo wp_kses_post( sprintf( __( '%s signup revenue this month', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $report_data->signup_revenue ) . '</strong>' ) ); ?>
<?php
// translators: %s: formatted amount.
echo wp_kses_post( sprintf( __( '%s signup revenue this month', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $report_data->signup_revenue ) . '</strong>' ) );
?>
</a>
</li>
<li class="renewal-count">
@@ -228,7 +231,10 @@ class WCS_Report_Dashboard {
</li>
<li class="renewal-revenue">
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-reports&tab=subscriptions&report=subscription_events_by_date&range=month' ) ); ?>">
<?php echo wp_kses_post( sprintf( __( '%s renewal revenue this month', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $report_data->renewal_revenue ) . '</strong>' ) ); ?>
<?php
// translators: %s: formatted amount.
echo wp_kses_post( sprintf( __( '%s renewal revenue this month', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $report_data->renewal_revenue ) . '</strong>' ) );
?>
</a>
</li>
<li class="cancel-count">

View File

View File

@@ -95,9 +95,13 @@ class WCS_Report_Subscription_By_Customer extends WP_List_Table {
public function get_columns() {
$columns = array(
'customer_name' => __( 'Customer', 'woocommerce-subscriptions' ),
// translators: %s: help tip.
'active_subscription_count' => sprintf( __( 'Active Subscriptions %s', 'woocommerce-subscriptions' ), wcs_help_tip( __( 'The number of subscriptions this customer has with a status of active or pending cancellation.', 'woocommerce-subscriptions' ) ) ),
// translators: %s: help tip.
'total_subscription_count' => sprintf( __( 'Total Subscriptions %s', 'woocommerce-subscriptions' ), wcs_help_tip( __( 'The number of subscriptions this customer has with a status other than pending or trashed.', 'woocommerce-subscriptions' ) ) ),
// translators: %s: help tip.
'total_subscription_order_count' => sprintf( __( 'Total Subscription Orders %s', 'woocommerce-subscriptions' ), wcs_help_tip( __( 'The number of sign-up, switch and renewal orders this customer has placed with your store with a paid status (i.e. processing or complete).', 'woocommerce-subscriptions' ) ) ),
// translators: %s: help tip.
'customer_lifetime_value' => sprintf( __( 'Lifetime Value from Subscriptions %s', 'woocommerce-subscriptions' ), wcs_help_tip( __( 'The total value of this customer\'s sign-up, switch and renewal orders.', 'woocommerce-subscriptions' ) ) ),
);
@@ -212,7 +216,6 @@ class WCS_Report_Subscription_By_Customer extends WP_List_Table {
"SELECT COUNT( DISTINCT customer_ids.meta_value) as total_customers,
COUNT(subscription_posts.ID) as total_subscriptions,
COALESCE( SUM(parent_total.meta_value), 0) as initial_order_total,
COALESCE( SUM(parent_total.meta_value), 0) as initial_order_total,
COUNT(DISTINCT parent_order.ID) as initial_order_count,
COALESCE(SUM(CASE
WHEN subscription_posts.post_status

View File

@@ -87,8 +87,11 @@ class WCS_Report_Subscription_By_Product extends WP_List_Table {
$columns = array(
'product_name' => __( 'Subscription Product', 'woocommerce-subscriptions' ),
// translators: %s: help tip.
'subscription_count' => sprintf( __( 'Subscription Count %s', 'woocommerce-subscriptions' ), wcs_help_tip( __( 'The number of subscriptions that include this product as a line item and have a status other than pending or trashed.', 'woocommerce-subscriptions' ) ) ),
// translators: %s: help tip.
'average_recurring_total' => sprintf( __( 'Average Recurring Line Total %s', 'woocommerce-subscriptions' ), wcs_help_tip( __( 'The average line total for this product on each subscription.', 'woocommerce-subscriptions' ) ) ),
// translators: %s: help tip.
'average_lifetime_value' => sprintf( __( 'Average Lifetime Value %s', 'woocommerce-subscriptions' ), wcs_help_tip( __( 'The average line total on all orders for this product line item.', 'woocommerce-subscriptions' ) ) ),
);

View File

@@ -269,7 +269,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$cached_results = get_transient( strtolower( get_class( $this ) ) );
// Check if we need to update the cache with the query results from the figures generated by get_order_report_data().
foreach ( array( 'new_subscriptions' => 'new_subscriptions', 'renewals' => 'renewal', 'resubscribes' => 'resubscribe', 'switches' => 'switch' ) as $report => $property_key ) {
foreach ( array( 'new_subscriptions' => 'new_subscriptions', 'renewals' => 'renewal', 'resubscribes' => 'resubscribe', 'switches' => 'switch' ) as $report => $property_key ) { // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
$query_hash = $this->report_data->{"{$report}_query_hash"};
if ( ! isset( $cached_results[ $query_hash ] ) ) {
$cached_results[ $query_hash ] = $this->report_data->{"{$property_key}_data"};
@@ -475,13 +475,16 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
/**
* Get the legend for the main chart sidebar
*
* @return array
*/
public function get_chart_legend() {
$legend = array();
$data = $this->get_report_data();
$tracks_id = 'report_subscription_events_by_date_';
$legend[] = array(
// translators: %s: formatted total amount.
'title' => sprintf( __( '%s signup revenue in this period', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $data->signup_orders_total_amount ) . '</strong>' ),
'placeholder' => __( 'The sum of all subscription parent orders, including other items, fees, tax and shipping.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['signup_total'],
@@ -489,6 +492,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
);
$legend[] = array(
// translators: %s: formatted total amount.
'title' => sprintf( __( '%s renewal revenue in this period', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $data->renewal_orders_total_amount ) . '</strong>' ),
'placeholder' => __( 'The sum of all renewal orders including tax and shipping.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['renewal_total'],
@@ -496,6 +500,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
);
$legend[] = array(
// translators: %s: formatted total amount.
'title' => sprintf( __( '%s resubscribe revenue in this period', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $data->resubscribe_orders_total_amount ) . '</strong>' ),
'placeholder' => __( 'The sum of all resubscribe orders including tax and shipping.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['resubscribe_total'],
@@ -503,6 +508,7 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
);
$legend[] = array(
// translators: %s: formatted total amount.
'title' => sprintf( __( '%s switch revenue in this period', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $data->switch_orders_total_amount ) . '</strong>' ),
'placeholder' => __( 'The sum of all switch orders including tax and shipping.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['switch_total'],
@@ -510,56 +516,154 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
);
$legend[] = array(
'title' => sprintf( __( '%2$s %1$s new subscriptions', 'woocommerce-subscriptions' ), '<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->new_subscription_total_count . '</span> </strong> </a>',
'<a href="' . esc_url( add_query_arg( array( 'post_type' => 'shop_subscription', '_subscriptions_list_key' => $this->report_data->new_subscriptions_query_hash, '_report' => strtolower( get_class( $this ) ) ), admin_url( 'edit.php' ) ) ). '">' ),
'title' => sprintf(
// translators: 2: link opening tag, 1: subscription count and closing tag.
__( '%2$s %1$s new subscriptions', 'woocommerce-subscriptions' ),
'<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->new_subscription_total_count . '</span> </strong> </a>',
'<a href="' .
esc_url(
add_query_arg(
array(
'post_type' => 'shop_subscription',
'_subscriptions_list_key' => $this->report_data->new_subscriptions_query_hash,
'_report' => strtolower( get_class( $this ) ),
),
admin_url( 'edit.php' )
)
) . '" id="' . $tracks_id . 'new">'
),
'placeholder' => __( 'The number of subscriptions created during this period, either by being manually created, imported or a customer placing an order. This includes orders pending payment.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['new_count'],
'highlight_series' => 1,
);
$legend[] = array(
'title' => sprintf( __( '%2$s %1$s subscription signups', 'woocommerce-subscriptions' ), '<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->signup_orders_total_count . '</strong> </a>',
'<a href="' . esc_url( add_query_arg( array( 'post_type' => 'shop_subscription', '_subscriptions_list_key' => $this->report_data->signup_orders_query_hash, '_report' => strtolower( get_class( $this ) ) ), admin_url( 'edit.php' ) ) ). '">' ),
'title' => sprintf(
// translators: 2: link opening tag, 1: subscription count and closing tag.
__( '%2$s %1$s subscription signups', 'woocommerce-subscriptions' ),
'<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->signup_orders_total_count . '</strong> </a>',
'<a href="' .
esc_url(
add_query_arg(
array(
'post_type' => 'shop_subscription',
'_subscriptions_list_key' => $this->report_data->signup_orders_query_hash,
'_report' => strtolower( get_class( $this ) ),
),
admin_url( 'edit.php' )
)
) . '" id="' . $tracks_id . 'signups">'
),
'placeholder' => __( 'The number of subscriptions purchased in parent orders created during this period. This represents the new subscriptions created by customers placing an order via checkout.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['signup_count'],
'highlight_series' => 2,
);
$legend[] = array(
'title' => sprintf( __( '%2$s %1$s subscription resubscribes', 'woocommerce-subscriptions' ), '<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->resubscribe_orders_total_count . '</strong> </a>',
'<a href="' . esc_url( add_query_arg( array( 'post_type' => 'shop_order', '_orders_list_key' => $this->report_data->resubscribes_query_hash, '_report' => strtolower( get_class( $this ) ) ), admin_url( 'edit.php' ) ) ). '">' ),
'title' => sprintf(
// translators: 2: link opening tag, 1: subscription count and closing tag.
__( '%2$s %1$s subscription resubscribes', 'woocommerce-subscriptions' ),
'<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->resubscribe_orders_total_count . '</strong> </a>',
'<a href="' .
esc_url(
add_query_arg(
array(
'post_type' => 'shop_order',
'_orders_list_key' => $this->report_data->resubscribes_query_hash,
'_report' => strtolower( get_class( $this ) ),
),
admin_url( 'edit.php' )
)
) . '" id="' . $tracks_id . 'resubscribes">'
),
'placeholder' => __( 'The number of resubscribe orders processed during this period.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['resubscribe_count'],
'highlight_series' => 3,
);
$legend[] = array(
'title' => sprintf( __( '%2$s %1$s subscription renewals', 'woocommerce-subscriptions' ), '<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->renewal_orders_total_count . '</strong> </a>',
'<a href="' . esc_url( add_query_arg( array( 'post_type' => 'shop_order', '_orders_list_key' => $this->report_data->renewals_query_hash, '_report' => strtolower( get_class( $this ) ) ), admin_url( 'edit.php' ) ) ). '">' ),
'title' => sprintf(
// translators: 2: link opening tag, 1: subscription count and closing tag.
__( '%2$s %1$s subscription renewals', 'woocommerce-subscriptions' ),
'<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->renewal_orders_total_count . '</strong> </a>',
'<a href="' .
esc_url(
add_query_arg(
array(
'post_type' => 'shop_order',
'_orders_list_key' => $this->report_data->renewals_query_hash,
'_report' => strtolower( get_class( $this ) ),
),
admin_url( 'edit.php' )
)
) . '" id="' . $tracks_id . 'renewals">'
),
'placeholder' => __( 'The number of renewal orders processed during this period.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['renewal_count'],
'highlight_series' => 4,
);
$legend[] = array(
'title' => sprintf( __( '%2$s %1$s subscription switches', 'woocommerce-subscriptions' ), '<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->switch_orders_total_count . '</strong> </a>',
'<a href="' . esc_url( add_query_arg( array( 'post_type' => 'shop_order', '_orders_list_key' => $this->report_data->switches_query_hash, '_report' => strtolower( get_class( $this ) ) ), admin_url( 'edit.php' ) ) ). '">' ),
'title' => sprintf(
// translators: 2: link opening tag, 1: subscription count and closing tag.
__( '%2$s %1$s subscription switches', 'woocommerce-subscriptions' ),
'<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->switch_orders_total_count . '</strong> </a>',
'<a href="' .
esc_url(
add_query_arg(
array(
'post_type' => 'shop_order',
'_orders_list_key' => $this->report_data->switches_query_hash,
'_report' => strtolower( get_class( $this ) ),
),
admin_url( 'edit.php' )
)
) . '" id="' . $tracks_id . 'switches">'
),
'placeholder' => __( 'The number of subscriptions upgraded, downgraded or cross-graded during this period.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['switch_count'],
'highlight_series' => 0,
);
$legend[] = array(
'title' => sprintf( __( '%2$s %1$s subscription cancellations', 'woocommerce-subscriptions' ), '<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->total_subscriptions_cancelled . '</strong> </a>',
'<a href="' . esc_url( add_query_arg( array( 'post_type' => 'shop_subscription', '_subscriptions_list_key' => $this->report_data->cancelled_subscriptions_query_hash, '_report' => strtolower( get_class( $this ) ) ), admin_url( 'edit.php' ) ) ). '">' ),
'title' => sprintf(
// translators: 2: link opening tag, 1: subscription count and closing tag.
__( '%2$s %1$s subscription cancellations', 'woocommerce-subscriptions' ),
'<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->total_subscriptions_cancelled . '</strong> </a>',
'<a href="' .
esc_url(
add_query_arg(
array(
'post_type' => 'shop_subscription',
'_subscriptions_list_key' => $this->report_data->cancelled_subscriptions_query_hash,
'_report' => strtolower( get_class( $this ) ),
),
admin_url( 'edit.php' )
)
) . '" id="' . $tracks_id . 'cancellations">'
),
'placeholder' => __( 'The number of subscriptions cancelled by the customer or store manager during this period. The pre-paid term may not yet have ended during this period.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['cancel_count'],
'highlight_series' => 7,
);
$legend[] = array(
'title' => sprintf( __( '%2$s %1$s ended subscriptions', 'woocommerce-subscriptions' ), '<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->total_subscriptions_ended . '</strong> </a>',
'<a href="' . esc_url( add_query_arg( array( 'post_type' => 'shop_subscription', '_subscriptions_list_key' => $this->report_data->ended_subscriptions_query_hash, '_report' => strtolower( get_class( $this ) ) ), admin_url( 'edit.php' ) ) ). '">' ),
'title' => sprintf(
// translators: 2: link opening tag, 1: subscription count and closing tag.
__( '%2$s %1$s ended subscriptions', 'woocommerce-subscriptions' ),
'<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->total_subscriptions_ended . '</strong> </a>',
'<a href="' .
esc_url(
add_query_arg(
array(
'post_type' => 'shop_subscription',
'_subscriptions_list_key' => $this->report_data->ended_subscriptions_query_hash,
'_report' => strtolower( get_class( $this ) ),
),
admin_url( 'edit.php' )
)
) . '" id="' . $tracks_id . 'ended">'
),
'placeholder' => __( 'The number of subscriptions which have either expired or reached the end of the prepaid term if it was previously cancelled.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['ended_count'],
'highlight_series' => 6,
@@ -569,8 +673,23 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$data_key = $this->report_data->subscriber_counts ? max( array_keys( $this->report_data->subscriber_counts ) ) : false;
$legend[] = array(
'title' => sprintf( __( '%2$s %1$s current subscriptions', 'woocommerce-subscriptions' ), '<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->total_subscriptions_at_period_end . '</strong> </a>',
'<a href="' . esc_url( add_query_arg( array( 'post_type' => 'shop_subscription', '_subscriptions_list_key' => $this->report_data->current_subscriptions_query_hash, '_report' => strtolower( get_class( $this ) ), '_data_key' => $data_key ), admin_url( 'edit.php' ) ) ). '">' ),
'title' => sprintf(
// translators: 2: link opening tag, 1: subscription count and closing tag.
__( '%2$s %1$s current subscriptions', 'woocommerce-subscriptions' ),
'<strong> <span class="woocommerce-subscriptions-count count">' . $this->report_data->total_subscriptions_at_period_end . '</strong> </a>',
'<a href="' .
esc_url(
add_query_arg(
array(
'post_type' => 'shop_subscription',
'_subscriptions_list_key' => $this->report_data->current_subscriptions_query_hash,
'_report' => strtolower( get_class( $this ) ),
'_data_key' => $data_key,
),
admin_url( 'edit.php' )
)
) . '" id="' . $tracks_id . 'current">'
),
'placeholder' => __( 'The number of subscriptions during this period with an end date in the future and a status other than pending.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['subscriber_count'],
'highlight_series' => 5,
@@ -578,8 +697,8 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$subscription_change_count = ( $data->total_subscriptions_at_period_end - $data->total_subscriptions_at_period_start > 0 ) ? '+' . ( $data->total_subscriptions_at_period_end - $data->total_subscriptions_at_period_start ) : ( $data->total_subscriptions_at_period_end - $data->total_subscriptions_at_period_start );
if ( $data->total_subscriptions_at_period_start === 0 ) {
$subscription_change_percent = '&#x221e;%'; // infinite percentage increase if the starting subs is 0
if ( 0 === $data->total_subscriptions_at_period_start ) {
$subscription_change_percent = '&#x221e;%'; // infinite percentage increase if the starting subs is 0.
} elseif ( $data->total_subscriptions_at_period_end - $data->total_subscriptions_at_period_start >= 0 ) {
$subscription_change_percent = '+' . number_format( ( ( ( $data->total_subscriptions_at_period_end - $data->total_subscriptions_at_period_start ) / $data->total_subscriptions_at_period_start ) * 100 ), 2 ) . '%';
} else {
@@ -587,8 +706,10 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
}
if ( $data->total_subscriptions_at_period_end - $data->total_subscriptions_at_period_start >= 0 ) {
// translators: %s: subscription net gain (with percentage).
$legend_title = __( '%s net subscription gain', 'woocommerce-subscriptions' );
} else {
// translators: %s: subscription net loss (with percentage).
$legend_title = __( '%s net subscription loss', 'woocommerce-subscriptions' );
}
@@ -1047,7 +1168,11 @@ class WCS_Report_Subscription_Events_By_Date extends WC_Admin_Report {
$time = strtotime( date( 'Ym', strtotime( "+{$i} MONTH", $start_date ) ) . '01' ) . '000';
if ( ! isset( $prepared_data[ $time ] ) ) {
$prepared_data[ $time ] = array( esc_js( $time ), 0, 'count' => 0 );
$prepared_data[ $time ] = array(
esc_js( $time ),
0,
'count' => 0,
);
}
}

View File

@@ -109,6 +109,7 @@ class WCS_Report_Subscription_Payment_Retry extends WC_Admin_Report {
$data = $this->get_report_data();
$legend[] = array(
// translators: %s: formatted amount.
'title' => sprintf( __( '%s renewal revenue recovered', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $data->renewal_total_amount ) . '</strong>' ),
'placeholder' => __( 'The total amount of revenue, including tax and shipping, recovered with the failed payment retry system for renewal orders with a failed payment.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['renewal_total'],
@@ -116,12 +117,14 @@ class WCS_Report_Subscription_Payment_Retry extends WC_Admin_Report {
);
$legend[] = array(
// translators: %s: renewal count.
'title' => sprintf( __( '%s renewal orders', 'woocommerce-subscriptions' ), '<strong>' . $data->renewal_total_count . '</strong>' ),
'placeholder' => __( 'The number of renewal orders which had a failed payment use the retry system.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['renewal_count'],
);
$legend[] = array(
// translators: %s: retry count.
'title' => sprintf( __( '%s retry attempts succeeded', 'woocommerce-subscriptions' ), '<strong>' . $data->retry_success_count . '</strong>' ),
'placeholder' => __( 'The number of renewal payment retries for this period which were able to process the payment which had previously failed one or more times.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['retry_success_count'],
@@ -129,6 +132,7 @@ class WCS_Report_Subscription_Payment_Retry extends WC_Admin_Report {
);
$legend[] = array(
// translators: %s: retry count.
'title' => sprintf( __( '%s retry attempts failed', 'woocommerce-subscriptions' ), '<strong>' . $data->retry_failed_count . '</strong>' ),
'placeholder' => __( 'The number of renewal payment retries for this period which did not result in a successful payment.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['retry_failure_count'],
@@ -136,6 +140,7 @@ class WCS_Report_Subscription_Payment_Retry extends WC_Admin_Report {
);
$legend[] = array(
// translators: %s: retry count.
'title' => sprintf( __( '%s retry attempts pending', 'woocommerce-subscriptions' ), '<strong>' . $data->retry_pending_count . '</strong>' ),
'placeholder' => __( 'The number of renewal payment retries not yet processed.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['retry_pending_count'],

View File

@@ -85,16 +85,21 @@ class WCS_Report_Upcoming_Recurring_Revenue extends WC_Admin_Report {
$this->average_sales = ( 0 != $total_renewal_count ? $total_renewal_revenue / $total_renewal_count : 0 );
$legend[] = array(
// translators: %s: formatted amount.
'title' => sprintf( __( '%s renewal income in this period', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $total_renewal_revenue ) . '</strong>' ),
'placeholder' => __( 'The sum of all the upcoming renewal orders, including items, fees, tax and shipping, for currently active subscriptions.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['renewals_amount'],
'highlight_series' => 1,
);
$legend[] = array(
// translators: %s: renewal count.
'title' => sprintf( __( '%s renewal orders', 'woocommerce-subscriptions' ), '<strong>' . $total_renewal_count . '</strong>' ),
'placeholder' => __( 'The number of upcoming renewal orders, for currently active subscriptions.', 'woocommerce-subscriptions' ),
'color' => $this->chart_colours['renewals_count'],
'highlight_series' => 0,
);
$legend[] = array(
// translators: %s: formatted amount.
'title' => sprintf( __( '%s average renewal amount', 'woocommerce-subscriptions' ), '<strong>' . wc_price( $this->average_sales ) . '</strong>' ),
'color' => $this->chart_colours['renewals_average'],
);

View File

0
includes/admin/views/html-report-by-period.php Executable file → Normal file
View File

0
includes/admin/wcs-admin-functions.php Executable file → Normal file
View File

View File

12
includes/api/class-wc-rest-subscriptions-controller.php Executable file → Normal file
View File

@@ -63,6 +63,7 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_V1_Controller {
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_statuses' ),
'permission_callback' => '__return_true',
),
'schema' => array( $this, 'get_public_item_schema' ),
) );
@@ -438,6 +439,9 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_V1_Controller {
}
}
break;
case 'transition_status':
$subscription->update_status( $value );
break;
case 'start_date':
case 'trial_end_date':
case 'next_payment_date':
@@ -460,6 +464,7 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_V1_Controller {
$subscription->update_dates( $dates_to_update );
}
} catch ( Exception $e ) {
// translators: placeholder is an error message.
throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_subscription_dates', sprintf( __( 'Updating subscription dates errored with message: %s', 'woocommerce-subscriptions' ), $e->getMessage() ), 400 );
}
@@ -484,6 +489,12 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_V1_Controller {
$schema = parent::get_item_schema();
$subscriptions_schema = array(
'transition_status' => array(
'description' => __( 'The status to transition the subscription to. Unlike the "status" param, this will calculate and update the subscription dates.', 'woocommerce-subscriptions' ),
'type' => 'string',
'enum' => $this->get_order_statuses(),
'context' => array( 'edit' ),
),
'billing_interval' => array(
'description' => __( 'The number of billing periods between subscription renewals.', 'woocommerce-subscriptions' ),
'type' => 'integer',
@@ -759,6 +770,7 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_V1_Controller {
$subscription->update_dates( $dates_to_update );
}
} catch ( Exception $e ) {
// translators: placeholder is an error message.
throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_subscription_dates', sprintf( __( 'Updating subscription dates errored with message: %s', 'woocommerce-subscriptions' ), $e->getMessage() ), 400 );
}
}

View File

10
includes/api/legacy/class-wc-api-subscriptions.php Executable file → Normal file
View File

@@ -212,7 +212,15 @@ class WC_API_Subscriptions extends WC_API_Orders {
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
} catch ( Exception $e ) {
$response = array( 'error', array( 'wcs_api_error_create_subscription' => array( 'message' => $e->getMessage(), 'status' => $e->getCode() ) ) );
$response = array(
'error',
array(
'wcs_api_error_create_subscription' => array(
'message' => $e->getMessage(),
'status' => $e->getCode(),
),
),
);
// show the subscription in response if it was still created but errored.
if ( ! empty( $subscription ) && ! is_wp_error( $subscription ) ) {

View File

View File

@@ -65,6 +65,7 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_statuses' ),
'permission_callback' => '__return_true',
),
'schema' => array( $this, 'get_public_item_schema' ),
) );
@@ -237,6 +238,7 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
$subscription = wcs_create_subscription( $args );
if ( is_wp_error( $subscription ) ) {
// translators: placeholder is an error message.
throw new WC_REST_Exception( 'woocommerce_rest_cannot_create_subscription', sprintf( __( 'Cannot create subscription: %s.', 'woocommerce-subscriptions' ), implode( ', ', $subscription->get_error_messages() ) ), 400 );
}
@@ -281,6 +283,7 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_Controller {
$subscription->update_dates( $dates_to_update );
}
} catch ( Exception $e ) {
// translators: placeholder is an error message.
throw new WC_REST_Exception( 'woocommerce_rest_cannot_update_subscription_dates', sprintf( __( 'Updating subscription dates errored with message: %s', 'woocommerce-subscriptions' ), $e->getMessage() ), 400 );
}
}

0
includes/class-wc-order-item-pending-switch.php Executable file → Normal file
View File

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