<?php /** * Privacy/GDPR related functionality which ties into WordPress functionality. * * @author Prospress * @category Class * @package WooCommerce Subscriptions\Privacy * @version 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.20 */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class WCS_Privacy extends WC_Abstract_Privacy { /** * Background updater to process personal data removal from subscriptions and related orders. * * @var WCS_Privacy_Background_Updater */ protected static $background_process; /** * A flag which is set when WC is doing a user inactivity cleanup. * Used to exclude subscription customers from the inactive user query. * * @var bool */ protected static $doing_user_inactivity_query = false; /** * WCS_Privacy constructor. */ public function __construct() { if ( ! self::$background_process ) { self::$background_process = new WCS_Privacy_Background_Updater(); } parent::__construct( __( 'WooCommerce Subscriptions', 'woocommerce-subscriptions' ) ); // Add our exporters and erasers. $this->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_privacy_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' ) ); // Hook in late so there is less opportunity for our flag to affect other user queries called on this hook. add_filter( 'woocommerce_delete_inactive_account_roles', array( __CLASS__, 'flag_subscription_user_exclusion_from_query' ), 1000 ); add_action( 'pre_get_users', array( __CLASS__, 'maybe_exclude_subscription_customers' ) ); add_filter( 'woocommerce_account_settings', array( __CLASS__, 'add_inactive_user_retention_note' ) ); add_action( 'handle_bulk_actions-woocommerce_page_wc-orders--shop_subscription', [ __CLASS__, 'handle_privacy_bulk_actions' ], 10, 3 ); } /** * Spawn events for subscription cleanup. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.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 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.20 */ public function get_privacy_message() { return '' . '<p>' . __( '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' ) . '</p>' . // translators: placeholders are opening and closing link tags, linking to additional privacy policy documentation. '<h3>' . __( 'What we collect and store', 'woocommerce-subscriptions' ) . '</h3>' . '<p>' . __( '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' ) . '</p>' . '<h3>' . __( 'What we share with others', 'woocommerce-subscriptions' ) . '</h3>' . '<p>' . __( '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' ) . '</p>' . // translators: placeholders are opening and closing link tags, linking to additional privacy policy documentation. '<p>' . sprintf( __( 'If you are using PayPal Standard or PayPal Reference transactions please see the %1$sPayPal Privacy Policy%2$s for more details.', 'woocommerce-subscriptions' ), '<a href="https://www.paypal.com/us/webapps/mpp/ua/privacy-full">', '</a>' ) . '</p>'; } /** * Adds the option to remove personal data from subscription via a bulk action. * * @since 5.2.0 * * @param array $bulk_actions Subscription bulk actions. * * @return array */ public static function add_privacy_bulk_action( $bulk_actions ) { $bulk_actions['wcs_remove_personal_data'] = __( 'Cancel and remove personal data', 'woocommerce-subscriptions' ); return $bulk_actions; } /** * Handles the Remove Personal Data bulk action requests for Subscriptions. * * @param string $redirect_url The default URL to redirect to after handling the bulk action request. * @param string $action The action to take against the list of subscriptions. * @param array $subscription_ids The list of subscription to run the action against. */ public static function handle_privacy_bulk_actions( $redirect_url, $action, $subscription_ids ) { if ( 'wcs_remove_personal_data' !== $action ) { return; } $changed = 0; $sendback_args = [ 'bulk_action' => 'wcs_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, $redirect_url ); wp_safe_redirect( esc_url_raw( $sendback ) ); exit(); } /** * Process the request to delete personal data from subscriptions via admin bulk action. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.20 */ public static function process_bulk_action() { /** * We only want to deal with shop_subscription bulk actions. * * Note: The nonce checks are ignored below as we are validating the request before returning. */ if ( ! isset( $_REQUEST['post_type'] ) || 'shop_subscription' !== $_REQUEST['post_type'] || ! isset( $_REQUEST['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return; } check_admin_referer( 'bulk-posts' ); $action = ''; if ( isset( $_REQUEST['action'] ) && -1 !== $_REQUEST['action'] ) { $action = wc_clean( wp_unslash( $_REQUEST['action'] ) ); } elseif ( isset( $_REQUEST['action2'] ) && -1 !== $_REQUEST['action2'] ) { $action = wc_clean( wp_unslash( $_REQUEST['action2'] ) ); } $subscription_ids = array_map( 'absint', (array) $_REQUEST['post'] ); $base_redirect_url = wp_get_referer() ? wp_get_referer() : ''; self::handle_privacy_bulk_actions( $base_redirect_url, $action, $subscription_ids ); } /** * Add admin notice after processing personal data removal bulk action. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.20 */ public static function bulk_admin_notices() { // Nonce verification is not required here because we're just displaying an admin notice after a verified request was made. if ( ! isset( $_REQUEST['bulk_action'] ) || ! 'wcs_remove_personal_data' === wc_clean( wp_unslash( $_REQUEST['bulk_action'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return; } if ( wcs_is_custom_order_tables_usage_enabled() ) { $current_screen = get_current_screen(); $is_subscription_list_table = $current_screen && wcs_get_page_screen_id( 'shop_subscription' ) === $current_screen->id; } else { global $post_type, $pagenow; $is_subscription_list_table = 'edit.php' === $pagenow && 'shop_subscription' === $post_type; } // Bail out if not on shop subscription list page. if ( ! $is_subscription_list_table ) { return; } $changed = isset( $_REQUEST['changed'] ) ? absint( $_REQUEST['changed'] ) : 0; // translators: %d: number of subscriptions affected. $message = sprintf( _n( 'Removed personal data from %d subscription.', 'Removed personal data from %d subscriptions.', $changed, 'woocommerce-subscriptions' ), number_format_i18n( $changed ) ); echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>'; } /** * Add a note to WC Personal Data Retention settings explaining that subscription orders aren't affected. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.20 * @param array $settings WooCommerce Account and Privacy settings. * @return array Account and Privacy settings. */ public static function add_caveat_to_order_data_retention_settings( $settings ) { if ( ! is_array( $settings ) ) { return $settings; } foreach ( $settings as &$setting ) { if ( isset( $setting['id'], $setting['type'] ) && 'personal_data_retention' === $setting['id'] && 'title' === $setting['type'] ) { // translators: placeholders are opening and closing tags. $note = sprintf( __( '%1$sNote:%2$s Orders which are related to subscriptions will not be included in the orders affected by these settings.', 'woocommerce-subscriptions' ), '<b>', '</b>' ); $setting['desc'] = isset( $setting['desc'] ) ? $setting['desc'] . '<br>' . $note : $note; } } return $settings; } /** * Add admin setting to turn subscription data removal when processing erasure requests on or off. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.20 * @param array $settings WooCommerce Account and Privacy settings. * @return array Account and Privacy settings. */ public static function add_subscription_data_retention_settings( $settings ) { if ( ! is_array( $settings ) ) { return $settings; } $erasure_text = esc_html__( 'account erasure request', 'woocommerce-subscriptions' ); if ( current_user_can( 'manage_privacy_options' ) ) { $erasure_text = sprintf( '<a href="%s">%s</a>', esc_url( admin_url( 'tools.php?page=remove_personal_data' ) ), $erasure_text ); } WC_Subscriptions_Admin::insert_setting_after( $settings, 'woocommerce_erasure_request_removes_order_data', array( 'desc' => __( 'Remove personal data from subscriptions', 'woocommerce-subscriptions' ), /* Translators: %s URL to erasure request screen. */ 'desc_tip' => sprintf( __( 'When handling an %s, should personal data within subscriptions be retained or removed?', 'woocommerce-subscriptions' ), $erasure_text ), 'id' => 'woocommerce_erasure_request_removes_subscription_data', 'type' => 'checkbox', 'default' => 'no', 'checkboxgroup' => '', 'autoload' => false, ) ); WC_Subscriptions_Admin::insert_setting_after( $settings, 'woocommerce_anonymize_completed_orders', array( 'title' => __( 'Retain ended subscriptions', 'woocommerce-subscriptions' ), 'desc_tip' => __( 'Retain ended subscriptions and their related orders for a specified duration before anonymizing the personal data within them.', 'woocommerce-subscriptions' ), 'id' => 'woocommerce_anonymize_ended_subscriptions', 'type' => 'relative_date_selector', 'placeholder' => __( 'N/A', 'woocommerce-subscriptions' ), 'default' => array( 'number' => '', 'unit' => 'months', ), 'autoload' => false, ) ); return $settings; } /** * Remove subscription related order types from the order anonymization query. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.20 * @param array $query_args @see wc_get_orders() args. * @return array The args used to get orders to anonymize. */ public static function remove_subscription_orders_from_anonymization_query( $query_args ) { if ( ! is_array( $query_args ) ) { return $query_args; } $query_args['subscription_parent'] = false; $query_args['subscription_renewal'] = false; $query_args['subscription_switch'] = false; $query_args['subscription_resubscribe'] = false; return $query_args; } /** * Add a note to the inactive user data retention setting noting that users with a subscription are excluded. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.4 * @param array $settings WooCommerce Account and Privacy settings. * @return array Account and Privacy settings. */ public static function add_inactive_user_retention_note( $settings ) { foreach ( $settings as &$setting ) { if ( isset( $setting['id'], $setting['desc_tip'] ) && 'woocommerce_delete_inactive_accounts' === $setting['id'] ) { $setting['desc_tip'] .= ' ' . __( 'Customers with a subscription are excluded from this setting.', 'woocommerce-subscriptions' ); break; } } return $settings; } /** * Set a flag to record inactive user account deletion. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.4 * @param array $user_roles The user roles included in the inactive user query. * @return array */ public static function flag_subscription_user_exclusion_from_query( $user_roles ) { self::$doing_user_inactivity_query = true; return $user_roles; } /** * Exclude customers who have subscriptions from the inactive user cleanup query. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.4 * @param WP_User_Query $user_query */ public static function maybe_exclude_subscription_customers( $user_query ) { if ( ! self::$doing_user_inactivity_query ) { return; } $user_query->set( 'exclude', array_merge( (array) $user_query->get( 'exclude' ), WC_Data_Store::load( 'subscription' )->get_subscription_customer_ids() ) ); self::$doing_user_inactivity_query = false; } /* Deprecated Functions */ /** * Add the option to remove personal data from subscription via a bulk action. * * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.20 * @param array $bulk_actions Subscription bulk actions. */ public static function add_remove_personal_data_bulk_action( $bulk_actions ) { wcs_deprecated_function( __METHOD__, 'subscriptions-core 5.2.0', 'WCS_Privacy_Exporters::add_privacy_bulk_action' ); $bulk_actions['remove_personal_data'] = __( 'Cancel and remove personal data', 'woocommerce-subscriptions' ); return $bulk_actions; } }