Commit 45ca31b75aa for woocommerce
commit 45ca31b75aa8413a4cd428a1ef12e5ca952a343b
Author: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>
Date: Thu Oct 15 19:34:03 2026 +0300
Record back-in-stock privacy erasures with their own cancellation source and keep sent rows sent (#69829)
fix: record privacy erasures with their own cancellation source and keep sent rows sent
The personal data eraser cancelled every matching back-in-stock sign-up
with the 'user' cancellation source, making GDPR erasures look like
voluntary opt-outs, and it also flipped already Sent rows to Cancelled.
Add a 'privacy' cancellation source, use it for Active and Pending
rows, and leave Sent and Cancelled rows at their status while still
anonymizing them.
diff --git a/plugins/woocommerce/changelog/fix-wooplug-7987-bis-privacy-eraser-source b/plugins/woocommerce/changelog/fix-wooplug-7987-bis-privacy-eraser-source
new file mode 100644
index 00000000000..2188bbcfdf6
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-wooplug-7987-bis-privacy-eraser-source
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Keep erased back-in-stock sign-ups distinct from customer opt-outs and leave sent ones marked as sent.
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Enums/NotificationCancellationSource.php b/plugins/woocommerce/src/Internal/StockNotifications/Enums/NotificationCancellationSource.php
index c67521dae4f..f11afa5732d 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Enums/NotificationCancellationSource.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Enums/NotificationCancellationSource.php
@@ -30,6 +30,15 @@ final class NotificationCancellationSource {
*/
public const SYSTEM = 'system';
+ /**
+ * Privacy cancellation source, set when a personal data erasure request anonymizes the notification.
+ *
+ * @since 11.3.0
+ *
+ * @var string
+ */
+ public const PRIVACY = 'privacy';
+
/**
* Get valid cancellation sources.
*
@@ -40,6 +49,7 @@ final class NotificationCancellationSource {
self::ADMIN,
self::USER,
self::SYSTEM,
+ self::PRIVACY,
);
}
}
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Privacy/PrivacyEraser.php b/plugins/woocommerce/src/Internal/StockNotifications/Privacy/PrivacyEraser.php
index 16bb72b407d..2489ef7b61a 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Privacy/PrivacyEraser.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Privacy/PrivacyEraser.php
@@ -42,7 +42,8 @@ class PrivacyEraser extends \WC_Abstract_Privacy {
/**
* Erase customer stock notification data for a given email address and the account it belongs to.
*
- * This method anonymizes the user email and sets the status of the notifications to 'cancelled'.
+ * This method anonymizes the user email and cancels Active and Pending notifications with the
+ * 'privacy' cancellation source. Sent and already Cancelled notifications keep their status.
*
* @param string $email_address The email address to erase data for.
*
@@ -90,9 +91,11 @@ class PrivacyEraser extends \WC_Abstract_Privacy {
$anonymous_email = wp_privacy_anonymize_data( 'email', $email_address );
$notification->set_user_email( $anonymous_email );
$notification->set_user_id( 0 );
- $notification->set_status( NotificationStatus::CANCELLED );
- $notification->set_cancellation_source( NotificationCancellationSource::USER );
- $notification->set_date_cancelled( time() );
+ if ( in_array( $notification->get_status(), array( NotificationStatus::ACTIVE, NotificationStatus::PENDING ), true ) ) {
+ $notification->set_status( NotificationStatus::CANCELLED );
+ $notification->set_cancellation_source( NotificationCancellationSource::PRIVACY );
+ $notification->set_date_cancelled( time() );
+ }
$notification->update_meta_data( '_anonymized', 'yes' );
$notification->update_meta_data( 'verification_action_key', '' );
$notification->update_meta_data( 'unsubscribe_action_key', '' );
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Privacy/PrivacyEraserTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Privacy/PrivacyEraserTests.php
index 0dcb9771bd6..2664324fb70 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Privacy/PrivacyEraserTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Privacy/PrivacyEraserTests.php
@@ -3,6 +3,7 @@
declare( strict_types = 1 );
namespace Automattic\WooCommerce\Tests\Internal\StockNotifications\Privacy;
+use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationCancellationSource;
use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
use Automattic\WooCommerce\Internal\StockNotifications\Privacy\PrivacyEraser;
use Automattic\WooCommerce\Internal\StockNotifications\Notification;
@@ -48,6 +49,53 @@ class PrivacyEraserTests extends \WC_Unit_Test_Case {
$this->assertEquals( $anonymous_notification->get_user_email(), wp_privacy_anonymize_data( 'email', '' ) );
$this->assertEquals( NotificationStatus::CANCELLED, $anonymous_notification->get_status() );
+ $this->assertEquals( NotificationCancellationSource::PRIVACY, $anonymous_notification->get_cancellation_source() );
+ $this->assertNotNull( $anonymous_notification->get_date_cancelled() );
+ }
+
+ /**
+ * @testdox Should keep a sent notification sent, with its sent date, while still anonymizing it.
+ */
+ public function test_privacy_eraser_keeps_sent_notification_sent(): void {
+ $notification = new Notification();
+ $notification->set_user_email( 'jon@doe.com' );
+ $notification->set_product_id( 1 );
+ $notification->set_status( NotificationStatus::SENT );
+ $notification->set_date_notified( time() - DAY_IN_SECONDS );
+ $notification_id = $notification->save();
+ $date_notified = ( new Notification( $notification_id ) )->get_date_notified();
+
+ $response = PrivacyEraser::erase_notification_data( 'jon@doe.com' );
+ $this->assertTrue( $response['items_removed'] );
+
+ $updated = new Notification( $notification_id );
+ $this->assertEquals( NotificationStatus::SENT, $updated->get_status() );
+ $this->assertNotNull( $date_notified );
+ $this->assertEquals( $date_notified->getTimestamp(), $updated->get_date_notified()->getTimestamp() );
+ $this->assertNull( $updated->get_cancellation_source() );
+ $this->assertNull( $updated->get_date_cancelled() );
+ $this->assertEquals( wp_privacy_anonymize_data( 'email', '' ), $updated->get_user_email() );
+ $this->assertSame( 0, $updated->get_user_id() );
+ $this->assertEquals( 'yes', $updated->get_meta( '_anonymized' ) );
+ }
+
+ /**
+ * @testdox Should keep the cancellation source of a notification the user already cancelled.
+ */
+ public function test_privacy_eraser_keeps_existing_cancellation_source(): void {
+ $notification = new Notification();
+ $notification->set_user_email( 'jon@doe.com' );
+ $notification->set_product_id( 1 );
+ $notification->set_status( NotificationStatus::CANCELLED );
+ $notification->set_cancellation_source( NotificationCancellationSource::USER );
+ $notification_id = $notification->save();
+
+ PrivacyEraser::erase_notification_data( 'jon@doe.com' );
+
+ $updated = new Notification( $notification_id );
+ $this->assertEquals( NotificationStatus::CANCELLED, $updated->get_status() );
+ $this->assertEquals( NotificationCancellationSource::USER, $updated->get_cancellation_source() );
+ $this->assertEquals( wp_privacy_anonymize_data( 'email', '' ), $updated->get_user_email() );
}
/**