diff --git a/assets/css/about.css b/assets/css/about.css
old mode 100644
new mode 100755
diff --git a/assets/css/admin.css b/assets/css/admin.css
old mode 100644
new mode 100755
diff --git a/assets/css/checkout.css b/assets/css/checkout.css
old mode 100644
new mode 100755
diff --git a/assets/css/dashboard.css b/assets/css/dashboard.css
old mode 100644
new mode 100755
diff --git a/assets/css/view-subscription.css b/assets/css/view-subscription.css
old mode 100644
new mode 100755
diff --git a/assets/css/wcs-upgrade.css b/assets/css/wcs-upgrade.css
old mode 100644
new mode 100755
diff --git a/assets/images/add-edit-subscription-screen.png b/assets/images/add-edit-subscription-screen.png
new file mode 100755
index 0000000..183d79f
Binary files /dev/null and b/assets/images/add-edit-subscription-screen.png differ
diff --git a/assets/images/admin-change-payment-method.jpg b/assets/images/admin-change-payment-method.jpg
new file mode 100755
index 0000000..19df347
Binary files /dev/null and b/assets/images/admin-change-payment-method.jpg differ
diff --git a/assets/images/ajax-loader.gif b/assets/images/ajax-loader.gif
old mode 100644
new mode 100755
diff --git a/assets/images/ajax-loader@2x.gif b/assets/images/ajax-loader@2x.gif
old mode 100644
new mode 100755
diff --git a/assets/images/billing-schedules-meta-box.png b/assets/images/billing-schedules-meta-box.png
new file mode 100755
index 0000000..b156012
Binary files /dev/null and b/assets/images/billing-schedules-meta-box.png differ
diff --git a/assets/images/checkout-recurring-totals.png b/assets/images/checkout-recurring-totals.png
new file mode 100755
index 0000000..150869b
Binary files /dev/null and b/assets/images/checkout-recurring-totals.png differ
diff --git a/assets/images/drip-downloadable-content.jpg b/assets/images/drip-downloadable-content.jpg
new file mode 100755
index 0000000..f761dc1
Binary files /dev/null and b/assets/images/drip-downloadable-content.jpg differ
diff --git a/assets/images/gift-subscription.png b/assets/images/gift-subscription.png
new file mode 100755
index 0000000..ca5db9f
Binary files /dev/null and b/assets/images/gift-subscription.png differ
diff --git a/assets/images/renewal-retry-settings.png b/assets/images/renewal-retry-settings.png
new file mode 100755
index 0000000..8d34c90
Binary files /dev/null and b/assets/images/renewal-retry-settings.png differ
diff --git a/assets/images/subscribe-all-the-things.png b/assets/images/subscribe-all-the-things.png
new file mode 100755
index 0000000..ead6c01
Binary files /dev/null and b/assets/images/subscribe-all-the-things.png differ
diff --git a/assets/images/subscription-reports.png b/assets/images/subscription-reports.png
new file mode 100755
index 0000000..3e86454
Binary files /dev/null and b/assets/images/subscription-reports.png differ
diff --git a/assets/images/subscription-suspended-email.jpg b/assets/images/subscription-suspended-email.jpg
new file mode 100755
index 0000000..9a2a174
Binary files /dev/null and b/assets/images/subscription-suspended-email.jpg differ
diff --git a/assets/images/subscriptions-importer-exporter.png b/assets/images/subscriptions-importer-exporter.png
new file mode 100755
index 0000000..909f562
Binary files /dev/null and b/assets/images/subscriptions-importer-exporter.png differ
diff --git a/assets/images/view-subscription.png b/assets/images/view-subscription.png
new file mode 100755
index 0000000..2e9d035
Binary files /dev/null and b/assets/images/view-subscription.png differ
diff --git a/assets/images/woocommerce_subscriptions_logo.png b/assets/images/woocommerce_subscriptions_logo.png
old mode 100644
new mode 100755
diff --git a/assets/js/admin/admin-pointers.js b/assets/js/admin/admin-pointers.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/admin.js b/assets/js/admin/admin.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/jquery.flot.axislabels.js b/assets/js/admin/jquery.flot.axislabels.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/jquery.flot.axislabels.min.js b/assets/js/admin/jquery.flot.axislabels.min.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/jquery.flot.orderBars.js b/assets/js/admin/jquery.flot.orderBars.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/jquery.flot.orderBars.min.js b/assets/js/admin/jquery.flot.orderBars.min.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/jstz.js b/assets/js/admin/jstz.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/jstz.min.js b/assets/js/admin/jstz.min.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/meta-boxes-subscription.js b/assets/js/admin/meta-boxes-subscription.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/moment.js b/assets/js/admin/moment.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/moment.min.js b/assets/js/admin/moment.min.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/reports.js b/assets/js/admin/reports.js
old mode 100644
new mode 100755
diff --git a/assets/js/admin/wcs-meta-boxes-order.js b/assets/js/admin/wcs-meta-boxes-order.js
old mode 100644
new mode 100755
diff --git a/assets/js/wcs-upgrade.js b/assets/js/wcs-upgrade.js
old mode 100644
new mode 100755
diff --git a/changelog.txt b/changelog.txt
old mode 100644
new mode 100755
index 8cf94aa..b2ce373
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,5 +1,19 @@
*** WooCommerce Subscriptions Changelog ***
+2018.05.29 - version 2.2.21
+* Tweak: Update WooCommerce tested up to compatibility flag.
+* Fix: [GDPR] While processing erasure requests anonymise all user subscriptions with any status. PR#2720
+* Fix: Fix PHP warnings which appear on the WooCommerce > Settings > Accounts & Privacy page and while exporting user personal data. PR#2719
+
+2018.05.25 - version 2.2.20
+* New: [GDPR] Export and erase subscription personal data when processing user export and erasure requests. PR#2692
+* New: [GDPR] Add an automatic data removal system for ended subscriptions and their related orders. PR#2702
+* New: [GDPR] Add details about the personal data the plugin collects to the privacy policy guide. PR#2700
+* Tweak: Reorder wcs_get_subscriptions_for_order logic to be more performance friendly. PR#2697
+* Tweak: Replace remote image sources with an internal asset. PR#2690
+* Fix: [WC3.4] Prevent subscription related orders from being trashed/anonymised by WC's automatic clean up system. PR#2698
+* Fix: [WC3.4] Only trigger the woocommerce_before_add_to_cart_button action hook once on variable subscription single product pages. PR#2706
+
2018.04.04 - version 2.2.19
* Fix: With Mixed Checkout disabled, only remove the add to cart notice when redirected straight to checkout. Fixes issue where error notices are removed incorrectly. PR#2541
* Fix: With Mixed Checkout disabled, only empty cart when the variation ID is the same but with different attributes. PR#2540
diff --git a/composer.lock b/composer.lock
old mode 100644
new mode 100755
diff --git a/includes/abstracts/abstract-wcs-cache-manager.php b/includes/abstracts/abstract-wcs-cache-manager.php
old mode 100644
new mode 100755
diff --git a/includes/abstracts/abstract-wcs-dynamic-hook-deprecator.php b/includes/abstracts/abstract-wcs-dynamic-hook-deprecator.php
old mode 100644
new mode 100755
diff --git a/includes/abstracts/abstract-wcs-hook-deprecator.php b/includes/abstracts/abstract-wcs-hook-deprecator.php
old mode 100644
new mode 100755
diff --git a/includes/abstracts/abstract-wcs-retry-store.php b/includes/abstracts/abstract-wcs-retry-store.php
old mode 100644
new mode 100755
diff --git a/includes/abstracts/abstract-wcs-scheduler.php b/includes/abstracts/abstract-wcs-scheduler.php
old mode 100644
new mode 100755
diff --git a/includes/admin/class-wc-subscriptions-admin.php b/includes/admin/class-wc-subscriptions-admin.php
old mode 100644
new mode 100755
index 7aefda2..e34486f
--- a/includes/admin/class-wc-subscriptions-admin.php
+++ b/includes/admin/class-wc-subscriptions-admin.php
@@ -1724,6 +1724,36 @@ class WC_Subscriptions_Admin {
);
}
+ /**
+ * Insert a setting or an array of settings after another specific setting by its ID.
+ *
+ * @since 2.2.20
+ * @param array $settings The original list of settings.
+ * @param string $insert_after_setting_id The setting id to insert the new setting after.
+ * @param array $new_setting The new setting to insert. Can be a single setting or an array of settings.
+ * @param string $insert_type The type of insert to perform. Can be 'single_setting' or 'multiple_settings'. Optional. Defaults to a single setting insert.
+ */
+ public static function insert_setting_after( &$settings, $insert_after_setting_id, $new_setting, $insert_type = 'single_setting' ) {
+ if ( ! is_array( $settings ) ) {
+ return;
+ }
+
+ $original_settings = $settings;
+ $settings = array();
+
+ foreach ( $original_settings as $setting ) {
+ $settings[] = $setting;
+
+ if ( isset( $setting['id'] ) && $insert_after_setting_id === $setting['id'] ) {
+ if ( 'single_setting' === $insert_type ) {
+ $settings[] = $new_setting;
+ } else {
+ $settings = array_merge( $settings, $new_setting );
+ }
+ }
+ }
+ }
+
/**
* Outputs the contents of the "Renewal Orders" meta box.
*
diff --git a/includes/admin/class-wcs-admin-meta-boxes.php b/includes/admin/class-wcs-admin-meta-boxes.php
old mode 100644
new mode 100755
diff --git a/includes/admin/class-wcs-admin-post-types.php b/includes/admin/class-wcs-admin-post-types.php
old mode 100644
new mode 100755
diff --git a/includes/admin/class-wcs-admin-reports.php b/includes/admin/class-wcs-admin-reports.php
old mode 100644
new mode 100755
diff --git a/includes/admin/meta-boxes/class-wcs-meta-box-payment-retries.php b/includes/admin/meta-boxes/class-wcs-meta-box-payment-retries.php
old mode 100644
new mode 100755
diff --git a/includes/admin/meta-boxes/class-wcs-meta-box-related-orders.php b/includes/admin/meta-boxes/class-wcs-meta-box-related-orders.php
old mode 100644
new mode 100755
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
old mode 100644
new mode 100755
diff --git a/includes/admin/meta-boxes/class-wcs-meta-box-subscription-schedule.php b/includes/admin/meta-boxes/class-wcs-meta-box-subscription-schedule.php
old mode 100644
new mode 100755
diff --git a/includes/admin/meta-boxes/views/html-related-orders-row.php b/includes/admin/meta-boxes/views/html-related-orders-row.php
old mode 100644
new mode 100755
diff --git a/includes/admin/meta-boxes/views/html-related-orders-table.php b/includes/admin/meta-boxes/views/html-related-orders-table.php
old mode 100644
new mode 100755
diff --git a/includes/admin/meta-boxes/views/html-retries-table.php b/includes/admin/meta-boxes/views/html-retries-table.php
old mode 100644
new mode 100755
diff --git a/includes/admin/meta-boxes/views/html-subscription-schedule.php b/includes/admin/meta-boxes/views/html-subscription-schedule.php
old mode 100644
new mode 100755
diff --git a/includes/admin/reports/class-wcs-report-cache-manager.php b/includes/admin/reports/class-wcs-report-cache-manager.php
old mode 100644
new mode 100755
diff --git a/includes/admin/reports/class-wcs-report-dashboard.php b/includes/admin/reports/class-wcs-report-dashboard.php
old mode 100644
new mode 100755
diff --git a/includes/admin/reports/class-wcs-report-retention-rate.php b/includes/admin/reports/class-wcs-report-retention-rate.php
old mode 100644
new mode 100755
diff --git a/includes/admin/reports/class-wcs-report-subscription-by-customer.php b/includes/admin/reports/class-wcs-report-subscription-by-customer.php
old mode 100644
new mode 100755
diff --git a/includes/admin/reports/class-wcs-report-subscription-by-product.php b/includes/admin/reports/class-wcs-report-subscription-by-product.php
old mode 100644
new mode 100755
diff --git a/includes/admin/reports/class-wcs-report-subscription-events-by-date.php b/includes/admin/reports/class-wcs-report-subscription-events-by-date.php
old mode 100644
new mode 100755
diff --git a/includes/admin/reports/class-wcs-report-subscription-payment-retry.php b/includes/admin/reports/class-wcs-report-subscription-payment-retry.php
old mode 100644
new mode 100755
diff --git a/includes/admin/reports/class-wcs-report-upcoming-recurring-revenue.php b/includes/admin/reports/class-wcs-report-upcoming-recurring-revenue.php
old mode 100644
new mode 100755
diff --git a/includes/admin/views/html-report-by-period.php b/includes/admin/views/html-report-by-period.php
old mode 100644
new mode 100755
diff --git a/includes/admin/wcs-admin-functions.php b/includes/admin/wcs-admin-functions.php
old mode 100644
new mode 100755
diff --git a/includes/api/class-wc-rest-subscription-notes-controller.php b/includes/api/class-wc-rest-subscription-notes-controller.php
old mode 100644
new mode 100755
diff --git a/includes/api/class-wc-rest-subscriptions-controller.php b/includes/api/class-wc-rest-subscriptions-controller.php
old mode 100644
new mode 100755
diff --git a/includes/api/legacy/class-wc-api-subscriptions-customers.php b/includes/api/legacy/class-wc-api-subscriptions-customers.php
old mode 100644
new mode 100755
diff --git a/includes/api/legacy/class-wc-api-subscriptions.php b/includes/api/legacy/class-wc-api-subscriptions.php
old mode 100644
new mode 100755
diff --git a/includes/api/legacy/class-wc-rest-subscription-notes-controller.php b/includes/api/legacy/class-wc-rest-subscription-notes-controller.php
old mode 100644
new mode 100755
diff --git a/includes/api/legacy/class-wc-rest-subscriptions-controller.php b/includes/api/legacy/class-wc-rest-subscriptions-controller.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-order-item-pending-switch.php b/includes/class-wc-order-item-pending-switch.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-product-subscription-variation.php b/includes/class-wc-product-subscription-variation.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-product-subscription.php b/includes/class-wc-product-subscription.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-product-variable-subscription.php b/includes/class-wc-product-variable-subscription.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscription.php b/includes/class-wc-subscription.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-addresses.php b/includes/class-wc-subscriptions-addresses.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-cart.php b/includes/class-wc-subscriptions-cart.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-change-payment-gateway.php b/includes/class-wc-subscriptions-change-payment-gateway.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-checkout.php b/includes/class-wc-subscriptions-checkout.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-coupon.php b/includes/class-wc-subscriptions-coupon.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-email.php b/includes/class-wc-subscriptions-email.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-manager.php b/includes/class-wc-subscriptions-manager.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-order.php b/includes/class-wc-subscriptions-order.php
old mode 100644
new mode 100755
index 49c27ce..26e4201
--- a/includes/class-wc-subscriptions-order.php
+++ b/includes/class-wc-subscriptions-order.php
@@ -74,6 +74,8 @@ class WC_Subscriptions_Order {
// Autocomplete subscription orders when they only contain a synchronised subscription or a resubscribe
add_filter( 'woocommerce_payment_complete_order_status', __CLASS__ . '::maybe_autocomplete_order', 10, 2 );
+
+ add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', array( __CLASS__, 'add_subscription_order_query_args' ), 10, 2 );
}
/*
@@ -1083,6 +1085,70 @@ class WC_Subscriptions_Order {
return $new_order_status;
}
+ /**
+ * Map subscription related order arguments passed to @see wc_get_orders() to WP_Query args.
+ *
+ * @since 2.2.20
+ * @param array $query WP_Query arguments.
+ * @param array $args @see wc_get_orders() arguments.
+ * @return array The WP_Query query arguments.
+ */
+ public static function add_subscription_order_query_args( $query, $args ) {
+ $order_type_meta_key_map = array(
+ 'subscription_renewal' => '_subscription_renewal',
+ 'subscription_switch' => '_subscription_switch',
+ 'subscription_resubscribe' => '_subscription_resubscribe',
+ );
+
+ // Add meta query args when querying by subscription related orders.
+ foreach ( $order_type_meta_key_map as $order_type => $meta_key ) {
+ if ( ! isset( $args[ $order_type ] ) ) {
+ continue;
+ }
+
+ $value = $args[ $order_type ];
+ $meta_query = array(
+ 'key' => $meta_key,
+ 'value' => $value,
+ );
+
+ // Map the value type to the appropriate compare arg.
+ if ( empty( $value ) ) {
+ $meta_query['compare'] = 'NOT EXISTS';
+ unset( $meta_query['value'] );
+ } elseif ( true === $value ) {
+ $meta_query['compare'] = 'EXISTS';
+ unset( $meta_query['value'] );
+ } elseif ( is_array( $value ) ) {
+ $meta_query['compare'] = 'IN';
+ } else {
+ $meta_query['compare'] = '=';
+ }
+
+ $query['meta_query'][] = $meta_query;
+ }
+
+ // Add query args when querying by subscription parent orders.
+ if ( isset( $args['subscription_parent'] ) ) {
+ $value = $args['subscription_parent'];
+
+ // Map the value type to post_in/post__not_in arg
+ if ( empty( $value ) ) {
+ $query['post__not_in'] = array_values( wcs_get_subscription_orders() );
+ } elseif ( true === $value ) {
+ $query['post__in'] = array_values( wcs_get_subscription_orders() );
+ } elseif ( is_array( $value ) ) {
+ $query['post__in'] = array_keys( array_flip( array_filter( array_map( 'wp_get_post_parent_id', $value ) ) ) );
+ } else {
+ if ( $parent = wp_get_post_parent_id( $value ) ) {
+ $query['post__in'] = array( $parent );
+ }
+ }
+ }
+
+ return $query;
+ }
+
/* Deprecated Functions */
/**
diff --git a/includes/class-wc-subscriptions-product.php b/includes/class-wc-subscriptions-product.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-renewal-order.php b/includes/class-wc-subscriptions-renewal-order.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-switcher.php b/includes/class-wc-subscriptions-switcher.php
old mode 100644
new mode 100755
diff --git a/includes/class-wc-subscriptions-synchroniser.php b/includes/class-wc-subscriptions-synchroniser.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-action-scheduler.php b/includes/class-wcs-action-scheduler.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-api.php b/includes/class-wcs-api.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-auth.php b/includes/class-wcs-auth.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-cache-manager-tlc.php b/includes/class-wcs-cache-manager-tlc.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-cached-data-manager.php b/includes/class-wcs-cached-data-manager.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-cart-initial-payment.php b/includes/class-wcs-cart-initial-payment.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-cart-renewal.php b/includes/class-wcs-cart-renewal.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-cart-resubscribe.php b/includes/class-wcs-cart-resubscribe.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-cart-switch.php b/includes/class-wcs-cart-switch.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-change-payment-method-admin.php b/includes/class-wcs-change-payment-method-admin.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-download-handler.php b/includes/class-wcs-download-handler.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-failed-scheduled-action-manager.php b/includes/class-wcs-failed-scheduled-action-manager.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-limiter.php b/includes/class-wcs-limiter.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-my-account-payment-methods.php b/includes/class-wcs-my-account-payment-methods.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-query.php b/includes/class-wcs-query.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-remove-item.php b/includes/class-wcs-remove-item.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-retry-manager.php b/includes/class-wcs-retry-manager.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-select2.php b/includes/class-wcs-select2.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-template-loader.php b/includes/class-wcs-template-loader.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-user-change-status-handler.php b/includes/class-wcs-user-change-status-handler.php
old mode 100644
new mode 100755
diff --git a/includes/class-wcs-webhooks.php b/includes/class-wcs-webhooks.php
old mode 100644
new mode 100755
diff --git a/includes/data-stores/class-wcs-subscription-data-store-cpt.php b/includes/data-stores/class-wcs-subscription-data-store-cpt.php
old mode 100644
new mode 100755
index 1f16ac1..8e25b19
--- a/includes/data-stores/class-wcs-subscription-data-store-cpt.php
+++ b/includes/data-stores/class-wcs-subscription-data-store-cpt.php
@@ -238,9 +238,9 @@ class WCS_Subscription_Data_Store_CPT extends WC_Order_Data_Store_CPT implements
// We only want IDs from the parent method
$parent_args['return'] = 'ids';
- $subscriptions = parent::get_orders( $parent_args );
+ $subscriptions = wc_get_orders( $parent_args );
- if ( $args['paginate'] ) {
+ if ( isset( $args['paginate'] ) && $args['paginate'] ) {
if ( 'objects' === $args['return'] ) {
$return = array_map( 'wcs_get_subscription', $subscriptions->orders );
diff --git a/includes/deprecated/class-wcs-action-deprecator.php b/includes/deprecated/class-wcs-action-deprecator.php
old mode 100644
new mode 100755
diff --git a/includes/deprecated/class-wcs-deprecated-filter-hooks.php b/includes/deprecated/class-wcs-deprecated-filter-hooks.php
old mode 100644
new mode 100755
diff --git a/includes/deprecated/class-wcs-dynamic-action-deprecator.php b/includes/deprecated/class-wcs-dynamic-action-deprecator.php
old mode 100644
new mode 100755
diff --git a/includes/deprecated/class-wcs-dynamic-filter-deprecator.php b/includes/deprecated/class-wcs-dynamic-filter-deprecator.php
old mode 100644
new mode 100755
diff --git a/includes/deprecated/class-wcs-filter-deprecator.php b/includes/deprecated/class-wcs-filter-deprecator.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-cancelled-subscription.php b/includes/emails/class-wcs-email-cancelled-subscription.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-customer-completed-renewal-order.php b/includes/emails/class-wcs-email-customer-completed-renewal-order.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-customer-completed-switch-order.php b/includes/emails/class-wcs-email-customer-completed-switch-order.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-customer-payment-retry.php b/includes/emails/class-wcs-email-customer-payment-retry.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-customer-processing-renewal-order.php b/includes/emails/class-wcs-email-customer-processing-renewal-order.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-customer-renewal-invoice.php b/includes/emails/class-wcs-email-customer-renewal-invoice.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-expired-subscription.php b/includes/emails/class-wcs-email-expired-subscription.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-new-renewal-order.php b/includes/emails/class-wcs-email-new-renewal-order.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-new-switch-order.php b/includes/emails/class-wcs-email-new-switch-order.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-on-hold-subscription.php b/includes/emails/class-wcs-email-on-hold-subscription.php
old mode 100644
new mode 100755
diff --git a/includes/emails/class-wcs-email-payment-retry.php b/includes/emails/class-wcs-email-payment-retry.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/class-wc-subscriptions-payment-gateways.php b/includes/gateways/class-wc-subscriptions-payment-gateways.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/class-wcs-paypal.php b/includes/gateways/paypal/class-wcs-paypal.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/abstracts/abstract-wcs-sv-api-base.php b/includes/gateways/paypal/includes/abstracts/abstract-wcs-sv-api-base.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/admin/class-wcs-paypal-admin.php b/includes/gateways/paypal/includes/admin/class-wcs-paypal-admin.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/admin/class-wcs-paypal-change-payment-method-admin.php b/includes/gateways/paypal/includes/admin/class-wcs-paypal-change-payment-method-admin.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-request.php b/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-request.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-response-billing-agreement.php b/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-response-billing-agreement.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-response-checkout.php b/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-response-checkout.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-response-payment.php b/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-response-payment.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-response-recurring-payment.php b/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-response-recurring-payment.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-response.php b/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-response.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api.php b/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-ipn-handler.php b/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-ipn-handler.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-standard-change-payment-method.php b/includes/gateways/paypal/includes/class-wcs-paypal-standard-change-payment-method.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-failure-handler.php b/includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-failure-handler.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php b/includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php b/includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-standard-switcher.php b/includes/gateways/paypal/includes/class-wcs-paypal-standard-switcher.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-status-manager.php b/includes/gateways/paypal/includes/class-wcs-paypal-status-manager.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-supports.php b/includes/gateways/paypal/includes/class-wcs-paypal-supports.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/deprecated/class-wc-paypal-standard-subscriptions.php b/includes/gateways/paypal/includes/deprecated/class-wc-paypal-standard-subscriptions.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/templates/admin-notices.php b/includes/gateways/paypal/includes/templates/admin-notices.php
old mode 100644
new mode 100755
diff --git a/includes/gateways/paypal/includes/wcs-paypal-functions.php b/includes/gateways/paypal/includes/wcs-paypal-functions.php
old mode 100644
new mode 100755
diff --git a/includes/legacy/class-wc-product-subscription-legacy.php b/includes/legacy/class-wc-product-subscription-legacy.php
old mode 100644
new mode 100755
diff --git a/includes/legacy/class-wc-product-subscription-variation-legacy.php b/includes/legacy/class-wc-product-subscription-variation-legacy.php
old mode 100644
new mode 100755
diff --git a/includes/legacy/class-wc-product-variable-subscription-legacy.php b/includes/legacy/class-wc-product-variable-subscription-legacy.php
old mode 100644
new mode 100755
diff --git a/includes/legacy/class-wc-subscription-legacy.php b/includes/legacy/class-wc-subscription-legacy.php
old mode 100644
new mode 100755
diff --git a/includes/legacy/class-wcs-array-property-post-meta-black-magic.php b/includes/legacy/class-wcs-array-property-post-meta-black-magic.php
old mode 100644
new mode 100755
diff --git a/includes/legacy/class-wcs-product-legacy.php b/includes/legacy/class-wcs-product-legacy.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/action-scheduler.php b/includes/libraries/action-scheduler/action-scheduler.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler.php b/includes/libraries/action-scheduler/classes/ActionScheduler.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_Action.php b/includes/libraries/action-scheduler/classes/ActionScheduler_Action.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_ActionClaim.php b/includes/libraries/action-scheduler/classes/ActionScheduler_ActionClaim.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_ActionFactory.php b/includes/libraries/action-scheduler/classes/ActionScheduler_ActionFactory.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_AdminView.php b/includes/libraries/action-scheduler/classes/ActionScheduler_AdminView.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_CronSchedule.php b/includes/libraries/action-scheduler/classes/ActionScheduler_CronSchedule.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_FatalErrorMonitor.php b/includes/libraries/action-scheduler/classes/ActionScheduler_FatalErrorMonitor.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_FinishedAction.php b/includes/libraries/action-scheduler/classes/ActionScheduler_FinishedAction.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_IntervalSchedule.php b/includes/libraries/action-scheduler/classes/ActionScheduler_IntervalSchedule.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_LogEntry.php b/includes/libraries/action-scheduler/classes/ActionScheduler_LogEntry.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_Logger.php b/includes/libraries/action-scheduler/classes/ActionScheduler_Logger.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_NullAction.php b/includes/libraries/action-scheduler/classes/ActionScheduler_NullAction.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_NullLogEntry.php b/includes/libraries/action-scheduler/classes/ActionScheduler_NullLogEntry.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_NullSchedule.php b/includes/libraries/action-scheduler/classes/ActionScheduler_NullSchedule.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_QueueCleaner.php b/includes/libraries/action-scheduler/classes/ActionScheduler_QueueCleaner.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php b/includes/libraries/action-scheduler/classes/ActionScheduler_QueueRunner.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_Schedule.php b/includes/libraries/action-scheduler/classes/ActionScheduler_Schedule.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_SimpleSchedule.php b/includes/libraries/action-scheduler/classes/ActionScheduler_SimpleSchedule.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_Store.php b/includes/libraries/action-scheduler/classes/ActionScheduler_Store.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_TimezoneHelper.php b/includes/libraries/action-scheduler/classes/ActionScheduler_TimezoneHelper.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_Versions.php b/includes/libraries/action-scheduler/classes/ActionScheduler_Versions.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_wpCommentLogger.php b/includes/libraries/action-scheduler/classes/ActionScheduler_wpCommentLogger.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore.php b/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore_PostStatusRegistrar.php b/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore_PostStatusRegistrar.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore_PostTypeRegistrar.php b/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore_PostTypeRegistrar.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore_TaxonomyRegistrar.php b/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore_TaxonomyRegistrar.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/composer.json b/includes/libraries/action-scheduler/composer.json
old mode 100644
new mode 100755
diff --git a/includes/libraries/action-scheduler/functions.php b/includes/libraries/action-scheduler/functions.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/class-wc-datetime.php b/includes/libraries/class-wc-datetime.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/tlc-transients/LICENSE b/includes/libraries/tlc-transients/LICENSE
old mode 100644
new mode 100755
diff --git a/includes/libraries/tlc-transients/class-tlc-transient-update-server.php b/includes/libraries/tlc-transients/class-tlc-transient-update-server.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/tlc-transients/class-tlc-transient.php b/includes/libraries/tlc-transients/class-tlc-transient.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/tlc-transients/composer.json b/includes/libraries/tlc-transients/composer.json
old mode 100644
new mode 100755
diff --git a/includes/libraries/tlc-transients/functions.php b/includes/libraries/tlc-transients/functions.php
old mode 100644
new mode 100755
diff --git a/includes/libraries/tlc-transients/tlc-transients.php b/includes/libraries/tlc-transients/tlc-transients.php
old mode 100644
new mode 100755
diff --git a/includes/payment-retry/class-wcs-retry-admin.php b/includes/payment-retry/class-wcs-retry-admin.php
old mode 100644
new mode 100755
diff --git a/includes/payment-retry/class-wcs-retry-email.php b/includes/payment-retry/class-wcs-retry-email.php
old mode 100644
new mode 100755
diff --git a/includes/payment-retry/class-wcs-retry-post-store.php b/includes/payment-retry/class-wcs-retry-post-store.php
old mode 100644
new mode 100755
diff --git a/includes/payment-retry/class-wcs-retry-rule.php b/includes/payment-retry/class-wcs-retry-rule.php
old mode 100644
new mode 100755
diff --git a/includes/payment-retry/class-wcs-retry-rules.php b/includes/payment-retry/class-wcs-retry-rules.php
old mode 100644
new mode 100755
diff --git a/includes/payment-retry/class-wcs-retry.php b/includes/payment-retry/class-wcs-retry.php
old mode 100644
new mode 100755
diff --git a/includes/privacy/class-wcs-privacy-background-updater.php b/includes/privacy/class-wcs-privacy-background-updater.php
new file mode 100755
index 0000000..38fef2f
--- /dev/null
+++ b/includes/privacy/class-wcs-privacy-background-updater.php
@@ -0,0 +1,219 @@
+ended_subscription_anonymization_hook, array( $this, 'anonymize_ended_subscriptions' ) );
+ add_action( $this->subscription_orders_anonymization_hook, array( $this, 'schedule_subscription_orders_anonymization_events' ), 10, 1 );
+ add_action( $this->order_anonymization_hook, array( $this, 'anonymize_order' ), 10, 1 );
+ }
+
+ /**
+ * Schedule ended subscription anonymization, if it's not already scheduled.
+ *
+ * @since 2.2.20
+ */
+ public function schedule_ended_subscription_anonymization() {
+ if ( false === wc_next_scheduled_action( $this->ended_subscription_anonymization_hook ) ) {
+ wc_schedule_single_action( time(), $this->ended_subscription_anonymization_hook );
+ }
+ }
+
+ /**
+ * Unschedule the ended subscription anonymization hook.
+ *
+ * @since 2.2.20
+ */
+ protected function unschedule_ended_subscription_anonymization() {
+ wc_unschedule_action( $this->ended_subscription_anonymization_hook );
+ }
+
+ /**
+ * Schedule subscription related order anonymization, if it's not already scheduled.
+ *
+ * @since 2.2.20
+ * @param int The subscription ID.
+ */
+ protected function schedule_subscription_orders_anonymization( $subscription_id ) {
+ $action_args = array( 'subscription_id' => intval( $subscription_id ) );
+
+ if ( false === wc_next_scheduled_action( $this->subscription_orders_anonymization_hook, $action_args ) ) {
+ wc_schedule_single_action( time(), $this->subscription_orders_anonymization_hook, $action_args );
+ }
+ }
+
+ /**
+ * Unschedule a specific subscription's related order anonymization hook.
+ *
+ * @since 2.2.20
+ * @param int The subscription ID.
+ */
+ protected function unschedule_subscription_orders_anonymization( $subscription_id ) {
+ wc_unschedule_action( $this->subscription_orders_anonymization_hook, array( 'subscription_id' => intval( $subscription_id ) ) );
+ }
+
+ /**
+ * Schedule a specific order's anonymization action.
+ *
+ * @since 2.2.20
+ * @param int The order ID.
+ */
+ protected function schedule_order_anonymization( $order_id ) {
+ wc_schedule_single_action( time(), $this->order_anonymization_hook, array( 'order_id' => intval( $order_id ) ) );
+ }
+
+ /**
+ * Check if an order has a scheduled anonymization action.
+ *
+ * @since 2.2.20
+ * @param int The order ID.
+ * @return bool Wether the order has a scheduled anonymization action.
+ */
+ protected function order_anonymization_is_scheduled( $order_id ) {
+ return false !== wc_next_scheduled_action( $this->order_anonymization_hook, array( 'order_id' => intval( $order_id ) ) );
+ }
+
+ /**
+ * Anonymize old ended subscriptions.
+ *
+ * @since 2.2.20
+ */
+ public function anonymize_ended_subscriptions() {
+ $option = wc_parse_relative_date_option( get_option( 'woocommerce_anonymize_ended_subscriptions' ) );
+
+ if ( empty( $option['number'] ) ) {
+ return;
+ }
+
+ // Reschedule the cleanup now just in case something goes wrong.
+ $this->schedule_ended_subscription_anonymization();
+
+ $batch_size = 20;
+
+ $subscriptions = wcs_get_subscriptions( array(
+ 'subscriptions_per_page' => $batch_size,
+ 'subscription_status' => wcs_get_subscription_ended_statuses(),
+ 'meta_query' => array(
+ array(
+ 'key' => '_schedule_end',
+ 'compare' => '<',
+ 'value' => gmdate( 'Y-m-d H:i:s', wc_string_to_timestamp( '-' . $option['number'] . ' ' . $option['unit'] ) ),
+ 'type' => 'DATETIME',
+ ),
+ array(
+ 'key' => '_anonymized',
+ 'compare' => 'NOT EXISTS',
+ ),
+ ),
+ ) );
+
+ foreach ( $subscriptions as $subscription ) {
+ if ( $subscription ) {
+ $this->schedule_subscription_orders_anonymization( $subscription->get_id() );
+ WCS_Privacy_Erasers::remove_subscription_personal_data( $subscription );
+ }
+ }
+
+ // If we haven't processed a full batch, we don't have any more subscriptions to process so there's no need to run any other batches.
+ if ( count( $subscriptions ) !== $batch_size ) {
+ $this->unschedule_ended_subscription_anonymization();
+ }
+ }
+
+ /**
+ * Schedule related order anonymization events for a specific subscription.
+ *
+ * @since 2.2.20
+ */
+ public function schedule_subscription_orders_anonymization_events( $subscription_id ) {
+ $subscription = wcs_get_subscription( $subscription_id );
+
+ if ( ! $subscription ) {
+ return;
+ }
+
+ // Reschedule the cleanup just in case something goes wrong.
+ $this->schedule_subscription_orders_anonymization( $subscription_id );
+
+ $related_orders = $subscription->get_related_orders( 'ids', array( 'parent', 'renewal', 'switch', 'resubscribe' ) );
+ $count = 0;
+ $limit = 20;
+
+ foreach ( $related_orders as $order_id ) {
+
+ // If this order already has an anonymization event scheduled, there's no need to proceed.
+ if ( $this->order_anonymization_is_scheduled( $order_id ) ) {
+ continue;
+ }
+
+ $order = wc_get_order( $order_id );
+
+ if ( ! is_a( $order, 'WC_Abstract_Order' ) || 'yes' === $order->get_meta( '_anonymized', true ) ) {
+ continue;
+ }
+
+ // We want to prevent orders being anonymized if there is a related subscription which hasn't been anonymized.
+ foreach ( wcs_get_subscriptions_for_order( $order, array( 'order_type' => 'any' ) ) as $subscription ) {
+ if ( 'yes' !== $subscription->get_meta( '_anonymized', true ) ) {
+ continue 2;
+ }
+ }
+
+ $this->schedule_order_anonymization( $order_id );
+ $count++;
+
+ if ( $count >= $limit ) {
+ break;
+ }
+ }
+
+ // If we haven't processed a full batch, we don't have any more related orders to process so there's no need to run any other batches.
+ if ( $limit > $count ) {
+ $this->unschedule_subscription_orders_anonymization( $subscription_id );
+ }
+ }
+
+ /**
+ * Anonymize an order.
+ *
+ * @param int The ID of the order to be anonymized.
+ * @since 2.2.20
+ */
+ public function anonymize_order( $order_id ) {
+ $order = wc_get_order( $order_id );
+
+ if ( $order ) {
+ WC_Privacy_Erasers::remove_order_personal_data( $order );
+ }
+ }
+}
diff --git a/includes/privacy/class-wcs-privacy-erasers.php b/includes/privacy/class-wcs-privacy-erasers.php
new file mode 100755
index 0000000..23fb080
--- /dev/null
+++ b/includes/privacy/class-wcs-privacy-erasers.php
@@ -0,0 +1,199 @@
+ 10,
+ 'page' => $page,
+ 'customer' => array( $email_address ),
+ 'status' => 'any',
+ );
+
+ if ( $user instanceof WP_User ) {
+ $subscription_args['customer'][] = (int) $user->ID;
+ }
+
+ // Use the data store get_orders() function as it supports getting subscriptions from billing email or customer ID - wcs_get_subscriptions() doesn't.
+ $subscriptions = WC_Data_Store::load( 'subscription' )->get_orders( $subscription_args );
+
+ return self::erase_subscription_data_and_generate_response( $subscriptions );
+ }
+
+ /**
+ * Erase personal data from an array of subscriptions and generate an eraser response.
+ *
+ * @since 2.2.20
+ * @param array $subscriptions An array of WC_Subscription objects.
+ * @param int $limit The number of subscriptions erased in each batch. Optional. Default is 10.
+ * @return array An array of response data to return to the WP eraser.
+ */
+ public static function erase_subscription_data_and_generate_response( $subscriptions, $limit = 10 ) {
+ $erasure_enabled = wc_string_to_bool( get_option( 'woocommerce_erasure_request_removes_subscription_data', 'no' ) );
+ $response = array(
+ 'items_removed' => false,
+ 'items_retained' => false,
+ 'messages' => array(),
+ 'done' => true,
+ );
+
+ if ( 0 < count( $subscriptions ) ) {
+ foreach ( $subscriptions as $subscription ) {
+ if ( apply_filters( 'woocommerce_privacy_erase_subscription_personal_data', $erasure_enabled, $subscription ) ) {
+ self::remove_subscription_personal_data( $subscription );
+
+ /* Translators: %s subscription number. */
+ $response['messages'][] = sprintf( __( 'Removed personal data from subscription %s.', 'woocommerce-subscriptions' ), $subscription->get_order_number() );
+ $response['items_removed'] = true;
+ } else {
+ /* Translators: %s subscription number. */
+ $response['messages'][] = sprintf( __( 'Personal data within subscription %s has been retained.', 'woocommerce-subscriptions' ), $subscription->get_order_number() );
+ $response['items_retained'] = true;
+ }
+ }
+
+ $response['done'] = $limit > count( $subscriptions );
+ }
+
+ return $response;
+ }
+
+ /**
+ * Remove personal data from a subscription object.
+ *
+ * Note; this will hinder the subscription's ability function correctly for obvious reasons!
+ *
+ * @since 2.2.20
+ * @param WC_Subscription $subscription $subscription object.
+ */
+ public static function remove_subscription_personal_data( $subscription ) {
+ $anonymized_data = array();
+
+ /**
+ * Allow extensions to remove their own personal data for this subscription first, so subscription data is still available.
+ *
+ * @since 2.2.20
+ * @param WC_Subscription $subscription A Subscription object.
+ */
+ do_action( 'woocommerce_privacy_before_remove_subscription_personal_data', $subscription );
+
+ // Cancel the subscription before removing personal data so payment gateways still have access to that data.
+ if ( $subscription->can_be_updated_to( 'cancelled' ) ) {
+ $subscription->update_status( 'cancelled' );
+ }
+
+ /**
+ * Expose props and data types we'll be anonymizing.
+ *
+ * @since 2.2.20
+ * @param array $props Keys are the prop names, values are the data type we'll be passing to wp_privacy_anonymize_data().
+ * @param WC_subscription $subscription A subscription object.
+ */
+ $props_to_remove = apply_filters( 'woocommerce_privacy_remove_subscription_personal_data_props', array(
+ 'customer_ip_address' => 'ip',
+ 'customer_user_agent' => 'text',
+ 'billing_first_name' => 'text',
+ 'billing_last_name' => 'text',
+ 'billing_company' => 'text',
+ 'billing_address_1' => 'text',
+ 'billing_address_2' => 'text',
+ 'billing_city' => 'text',
+ 'billing_postcode' => 'text',
+ 'billing_state' => 'address_state',
+ 'billing_country' => 'address_country',
+ 'billing_phone' => 'phone',
+ 'billing_email' => 'email',
+ 'shipping_first_name' => 'text',
+ 'shipping_last_name' => 'text',
+ 'shipping_company' => 'text',
+ 'shipping_address_1' => 'text',
+ 'shipping_address_2' => 'text',
+ 'shipping_city' => 'text',
+ 'shipping_postcode' => 'text',
+ 'shipping_state' => 'address_state',
+ 'shipping_country' => 'address_country',
+ 'customer_id' => 'numeric_id',
+ 'transaction_id' => 'numeric_id',
+ ), $subscription );
+
+ if ( ! empty( $props_to_remove ) && is_array( $props_to_remove ) ) {
+ foreach ( $props_to_remove as $prop => $data_type ) {
+ // Get the current value in edit context.
+ $value = $subscription->{"get_$prop"}( 'edit' );
+
+ // If the value is empty, it does not need to be anonymized.
+ if ( empty( $value ) || empty( $data_type ) ) {
+ continue;
+ }
+
+ if ( function_exists( 'wp_privacy_anonymize_data' ) ) {
+ $anon_value = wp_privacy_anonymize_data( $data_type, $value );
+ } else {
+ $anon_value = '';
+ }
+
+ /**
+ * Expose a way to control the anonymized value of a prop via 3rd party code.
+ *
+ * @since 2.2.20
+ * @param bool $anonymized_data Value of this prop after anonymization.
+ * @param string $prop Name of the prop being removed.
+ * @param string $value Current value of the data.
+ * @param string $data_type Type of data.
+ * @param WC_Subscription $subscription An subscription object.
+ */
+ $anonymized_data[ $prop ] = apply_filters( 'woocommerce_privacy_remove_subscription_personal_data_prop_value', $anon_value, $prop, $value, $data_type, $subscription );
+ }
+ }
+
+ // Set all new props and persist the new data to the database.
+ $subscription->set_props( $anonymized_data );
+ $subscription->update_meta_data( '_anonymized', 'yes' );
+ $subscription->save();
+
+ // Delete subscription notes which can contain PII.
+ $notes = wc_get_order_notes( array(
+ 'order_id' => $subscription->get_id(),
+ ) );
+
+ foreach ( $notes as $note ) {
+ wc_delete_order_note( $note->id );
+ }
+
+ // Add note that this event occurred.
+ $subscription->add_order_note( __( 'Personal data removed.', 'woocommerce-subscriptions' ) );
+
+ /**
+ * Allow extensions to remove their own personal data for this subscription.
+ *
+ * @since 2.2.20
+ * @param WC_subscription $subscription A subscription object.
+ */
+ do_action( 'woocommerce_privacy_remove_subscription_personal_data', $subscription );
+ }
+}
diff --git a/includes/privacy/class-wcs-privacy-exporters.php b/includes/privacy/class-wcs-privacy-exporters.php
new file mode 100755
index 0000000..1601ebf
--- /dev/null
+++ b/includes/privacy/class-wcs-privacy-exporters.php
@@ -0,0 +1,135 @@
+ 10,
+ 'page' => $page,
+ 'customer' => array( $email_address ),
+ 'status' => 'any',
+ );
+
+ if ( $user instanceof WP_User ) {
+ $subscription_args['customer'][] = (int) $user->ID;
+ }
+
+ // Use the data store get_orders() function as it supports getting subscriptions from billing email or customer ID - wcs_get_subscriptions() doesn't.
+ $subscriptions = WC_Data_Store::load( 'subscription' )->get_orders( $subscription_args );
+
+ if ( 0 < count( $subscriptions ) ) {
+ foreach ( $subscriptions as $subscription ) {
+ $data_to_export[] = array(
+ 'group_id' => 'woocommerce_subscriptions',
+ 'group_label' => __( 'Subscriptions', 'woocommerce-subscriptions' ),
+ 'item_id' => 'subscription-' . $subscription->get_id(),
+ 'data' => self::get_subscription_personal_data( $subscription ),
+ );
+ }
+ $done = 10 > count( $subscriptions );
+ } else {
+ $done = true;
+ }
+
+ return array(
+ 'data' => $data_to_export,
+ 'done' => $done,
+ );
+ }
+
+ /**
+ * Get personal data (key/value pairs) for an subscription object.
+ *
+ * @since 2.2.20
+ * @param WC_Subscription $subscription Subscription object.
+ * @return array
+ */
+ protected static function get_subscription_personal_data( $subscription ) {
+ $personal_data = array();
+ $props_to_export = apply_filters( 'woocommerce_privacy_export_subscription_personal_data_props', array(
+ 'order_number' => __( 'Subscription Number', 'woocommerce-subscriptions' ),
+ 'date_created' => __( 'Created Date', 'woocommerce-subscriptions' ),
+ 'total' => __( 'Recurring Total', 'woocommerce-subscriptions' ),
+ 'items' => __( 'Subscription Items', 'woocommerce-subscriptions' ),
+ 'customer_ip_address' => __( 'IP Address', 'woocommerce-subscriptions' ),
+ 'customer_user_agent' => __( 'Browser User Agent', 'woocommerce-subscriptions' ),
+ 'formatted_billing_address' => __( 'Billing Address', 'woocommerce-subscriptions' ),
+ 'formatted_shipping_address' => __( 'Shipping Address', 'woocommerce-subscriptions' ),
+ 'billing_phone' => __( 'Phone Number', 'woocommerce-subscriptions' ),
+ 'billing_email' => __( 'Email Address', 'woocommerce-subscriptions' ),
+ ), $subscription );
+
+ foreach ( $props_to_export as $prop => $name ) {
+ $value = '';
+
+ switch ( $prop ) {
+ case 'items':
+ $item_names = array();
+ foreach ( $subscription->get_items() as $item ) {
+ $item_names[] = $item->get_name() . ' x ' . $item->get_quantity();
+ }
+ $value = implode( ', ', $item_names );
+ break;
+ case 'date_created':
+ $value = wc_format_datetime( $subscription->get_date_created(), get_option( 'date_format' ) . ', ' . get_option( 'time_format' ) );
+ break;
+ case 'formatted_billing_address':
+ case 'formatted_shipping_address':
+ $value = preg_replace( '#
#i', ', ', $subscription->{"get_$prop"}() );
+ break;
+ default:
+ if ( is_callable( array( $subscription, 'get_' . $prop ) ) ) {
+ $value = $subscription->{"get_$prop"}();
+ }
+ break;
+ }
+
+ $value = apply_filters( 'woocommerce_privacy_export_subscription_personal_data_prop', $value, $prop, $subscription );
+
+ if ( $value ) {
+ $personal_data[] = array(
+ 'name' => $name,
+ 'value' => $value,
+ );
+ }
+ }
+
+ /**
+ * Allow extensions to register their own personal data for this subscription for the export.
+ *
+ * @since 2.2.20
+ * @param array $personal_data Array of name value pairs to expose in the export.
+ * @param WC_Subscription $subscription A subscription object.
+ */
+ $personal_data = apply_filters( 'woocommerce_privacy_export_subscription_personal_data', $personal_data, $subscription );
+
+ return $personal_data;
+ }
+}
diff --git a/includes/privacy/class-wcs-privacy.php b/includes/privacy/class-wcs-privacy.php
new file mode 100755
index 0000000..d145010
--- /dev/null
+++ b/includes/privacy/class-wcs-privacy.php
@@ -0,0 +1,250 @@
+add_exporter( 'woocommerce-subscriptions-data', __( 'Subscriptions Data', 'woocommerce-subscriptions' ), array( 'WCS_Privacy_Exporters', 'subscription_data_exporter' ) );
+ $this->add_eraser( 'woocommerce-subscriptions-data', __( 'Subscriptions Data', 'woocommerce-subscriptions' ), array( 'WCS_Privacy_Erasers', 'subscription_data_eraser' ) );
+ }
+
+ /**
+ * Attach callbacks.
+ */
+ public function init() {
+ parent::init();
+ self::$background_process->init();
+
+ add_filter( 'woocommerce_subscription_bulk_actions', array( __CLASS__, 'add_remove_personal_data_bulk_action' ) );
+ add_action( 'load-edit.php', array( __CLASS__, 'process_bulk_action' ) );
+ add_action( 'woocommerce_remove_subscription_personal_data', array( 'WCS_Privacy_Erasers', 'remove_subscription_personal_data' ) );
+ add_action( 'admin_notices', array( __CLASS__, 'bulk_admin_notices' ) );
+
+ add_filter( 'woocommerce_account_settings', array( __CLASS__, 'add_caveat_to_order_data_retention_settings' ) );
+ add_filter( 'woocommerce_account_settings', array( __CLASS__, 'add_subscription_data_retention_settings' ) );
+
+ // Attach callbacks to prevent subscription related orders being trashed or anonymized
+ add_filter( 'woocommerce_trash_pending_orders_query_args', array( __CLASS__, 'remove_subscription_orders_from_anonymization_query' ), 10, 2 );
+ add_filter( 'woocommerce_trash_failed_orders_query_args', array( __CLASS__, 'remove_subscription_orders_from_anonymization_query' ), 10, 2 );
+ add_filter( 'woocommerce_trash_cancelled_orders_query_args', array( __CLASS__, 'remove_subscription_orders_from_anonymization_query' ), 10, 2 );
+ add_filter( 'woocommerce_anonymize_completed_orders_query_args', array( __CLASS__, 'remove_subscription_orders_from_anonymization_query' ), 10, 2 );
+
+ add_action( 'woocommerce_cleanup_personal_data', array( $this, 'queue_cleanup_personal_data' ) );
+ }
+
+ /**
+ * Spawn events for subscription cleanup.
+ *
+ * @since 2.2.20
+ */
+ public function queue_cleanup_personal_data() {
+ self::$background_process->schedule_ended_subscription_anonymization();
+ }
+
+ /**
+ * Add privacy policy content for the privacy policy page.
+ *
+ * @since 2.2.20
+ */
+ public function get_privacy_message() {
+ return
+ '
' . __( 'By using WooCommerce Subscriptions, you may be storing personal data and depending on which third-party payment processors you’re using to take subscription payments, you may be sharing personal data with external sources.', 'woocommerce-subscriptions' ) . '
' . + // translators: placeholders are opening and closing link tags, linking to additional privacy policy documentation. + '' . __( 'For the purposes of processing recurring subscription payments, we store the customer\'s name, billing address, shipping address, email address, phone number and credit card/payment details.', 'woocommerce-subscriptions' ) . '
' . + '' . __( 'What personal information your store shares with external sources depends on which third-party payment processor plugins you are using to collect subscription payments. We recommend that you consult with their privacy policies to inform this section of your privacy policy.', 'woocommerce-subscriptions' ) . '
' . + // translators: placeholders are opening and closing link tags, linking to additional privacy policy documentation. + '' . sprintf( __( 'If you are using PayPal Standard or PayPal Reference transactions please see the %sPayPal Privacy Policy%s for more details.', 'woocommerce-subscriptions' ), '', '' ) . '
'; + } + + /** + * Add the option to remove personal data from subscription via a bulk action. + * + * @since 2.2.20 + * @param array $bulk_actions Subscription bulk actions. + */ + public static function add_remove_personal_data_bulk_action( $bulk_actions ) { + $bulk_actions['remove_personal_data'] = __( 'Cancel and remove personal data', 'woocommerce-subscriptions' ); + return $bulk_actions; + } + + /** + * Process the request to delete personal data from subscriptions via admin bulk action. + * + * @since 2.2.20 + */ + public static function process_bulk_action() { + + // We only want to deal with shop_subscription bulk actions. + if ( ! isset( $_REQUEST['post_type'] ) || 'shop_subscription' !== $_REQUEST['post_type'] || ! isset( $_REQUEST['post'] ) ) { + return; + } + + $action = ''; + + if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] ) { + $action = $_REQUEST['action']; + } else if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] ) { + $action = $_REQUEST['action2']; + } + + if ( 'remove_personal_data' !== $action ) { + return; + } + + $subscription_ids = array_map( 'absint', (array) $_REQUEST['post'] ); + $changed = 0; + $sendback_args = array( + 'post_type' => 'shop_subscription', + 'bulk_action' => 'remove_personal_data', + 'ids' => join( ',', $subscription_ids ), + 'error_count' => 0, + ); + + foreach ( $subscription_ids as $subscription_id ) { + $subscription = wcs_get_subscription( $subscription_id ); + + if ( is_a( $subscription, 'WC_Subscription' ) ) { + do_action( 'woocommerce_remove_subscription_personal_data', $subscription ); + $changed++; + } + } + + $sendback_args['changed'] = $changed; + $sendback = add_query_arg( $sendback_args, wp_get_referer() ? wp_get_referer() : '' ); + + wp_safe_redirect( esc_url_raw( $sendback ) ); + exit(); + } + + /** + * Add admin notice after processing personal data removal bulk action. + * + * @since 2.2.20 + */ + public static function bulk_admin_notices() { + global $post_type, $pagenow; + + if ( 'edit.php' !== $pagenow || 'shop_subscription' !== $post_type || ! isset( $_REQUEST['bulk_action'] ) || 'remove_personal_data' !== wc_clean( $_REQUEST['bulk_action'] ) ) { + return; + } + + $changed = isset( $_REQUEST['changed'] ) ? absint( $_REQUEST['changed'] ) : 0; + $message = sprintf( _n( 'Removed personal data from %d subscription.', 'Removed personal data from %d subscriptions.', $changed, 'woocommerce-subscriptions' ), number_format_i18n( $changed ) ); + echo '' . esc_html( $message ) . '
) tags @@ -117,7 +117,7 @@ $settings_page = admin_url( 'admin.php?page=wc-settings&tab=subscriptions' );
tags @@ -130,7 +130,7 @@ $settings_page = admin_url( 'admin.php?page=wc-settings&tab=subscriptions' );
tags diff --git a/includes/upgrades/templates/wcs-about.php b/includes/upgrades/templates/wcs-about.php old mode 100644 new mode 100755 index 45da673..74260e5 --- a/includes/upgrades/templates/wcs-about.php +++ b/includes/upgrades/templates/wcs-about.php @@ -42,7 +42,7 @@ if ( ! defined( 'ABSPATH' ) ) {