diff --git a/ruleset/phpstan.neon b/ruleset/phpstan.neon index 84c8a29..769dc4d 100644 --- a/ruleset/phpstan.neon +++ b/ruleset/phpstan.neon @@ -22,3 +22,4 @@ parameters: - '#Call to an undefined method Faker\\UniqueGenerator::format\(\).#' - "#^Call to an undefined method Symfony\\\\Component\\\\HttpFoundation\\\\Session\\\\SessionInterface\\:\\:getFlashBag\\(\\)\\.$#" - '#.*Laminas\\Stdlib\\PriorityQueue*.#' + - '#Call to an undefined method Faker\\UniqueGenerator::format\(\).#' diff --git a/src/Processor/AdvancedActions/AnonymizeCustomersNotLoggedBeforeProcessor.php b/src/Processor/AdvancedActions/AnonymizeCustomersNotLoggedBeforeProcessor.php index 7ead937..18f94bc 100644 --- a/src/Processor/AdvancedActions/AnonymizeCustomersNotLoggedBeforeProcessor.php +++ b/src/Processor/AdvancedActions/AnonymizeCustomersNotLoggedBeforeProcessor.php @@ -9,15 +9,29 @@ use Sylius\Component\Core\Model\ShopUserInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Form\FormInterface; +use Symfony\Component\HttpFoundation\RequestStack; use Synolia\SyliusGDPRPlugin\Processor\AnonymizerProcessor; class AnonymizeCustomersNotLoggedBeforeProcessor implements AdvancedActionsFormDataProcessorInterface { + private EntityManagerInterface $entityManager; + + private AnonymizerProcessor $anonymizerProcessor; + + private ParameterBagInterface $parameterBag; + + private RequestStack $requestStack; + public function __construct( - private EntityManagerInterface $entityManager, - private AnonymizerProcessor $anonymizerProcessor, - private ParameterBagInterface $parameterBag, + EntityManagerInterface $entityManager, + AnonymizerProcessor $anonymizerProcessor, + ParameterBagInterface $parameterBag, + RequestStack $requestStack, ) { + $this->entityManager = $entityManager; + $this->anonymizerProcessor = $anonymizerProcessor; + $this->parameterBag = $parameterBag; + $this->requestStack = $requestStack; } /** @inheritdoc */ @@ -43,6 +57,8 @@ public function process(string $formTypeClass, FormInterface $form): void } $this->anonymizerProcessor->anonymizeEntities($this->getCustomersFromShopUsers($shopUsers)); + + $this->requestStack->getSession()->getFlashBag()->add('success', sprintf('%d customers anonymized.', $this->anonymizerProcessor->getAnonymizedEntityCount())); } private function getCustomersFromShopUsers(array $shopUsers): array diff --git a/src/Processor/AdvancedActions/AnonymizeCustomersWithoutAnyOrdersBeforeProcessor.php b/src/Processor/AdvancedActions/AnonymizeCustomersWithoutAnyOrdersBeforeProcessor.php index a8e4ad3..c075987 100644 --- a/src/Processor/AdvancedActions/AnonymizeCustomersWithoutAnyOrdersBeforeProcessor.php +++ b/src/Processor/AdvancedActions/AnonymizeCustomersWithoutAnyOrdersBeforeProcessor.php @@ -10,15 +10,29 @@ use Sylius\Component\Core\Model\OrderInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Form\FormInterface; +use Symfony\Component\HttpFoundation\RequestStack; use Synolia\SyliusGDPRPlugin\Processor\AnonymizerProcessor; class AnonymizeCustomersWithoutAnyOrdersBeforeProcessor implements AdvancedActionsFormDataProcessorInterface { + private EntityManagerInterface $entityManager; + + private AnonymizerProcessor $anonymizerProcessor; + + private ParameterBagInterface $parameterBag; + + private RequestStack $requestStack; + public function __construct( - private EntityManagerInterface $entityManager, - private AnonymizerProcessor $anonymizerProcessor, - private ParameterBagInterface $parameterBag, + EntityManagerInterface $entityManager, + AnonymizerProcessor $anonymizerProcessor, + ParameterBagInterface $parameterBag, + RequestStack $requestStack, ) { + $this->entityManager = $entityManager; + $this->anonymizerProcessor = $anonymizerProcessor; + $this->parameterBag = $parameterBag; + $this->requestStack = $requestStack; } /** @inheritdoc */ @@ -46,6 +60,8 @@ public function process(string $formTypeClass, FormInterface $form): void $this->removeNoneEligibleCustomers($customers); $this->anonymizerProcessor->anonymizeEntities($customers); + + $this->requestStack->getSession()->getFlashBag()->add('success', sprintf('%d customers anonymized.', $this->anonymizerProcessor->getAnonymizedEntityCount())); } private function removeNoneEligibleCustomers(array &$customers): array diff --git a/src/Processor/AnonymizerProcessor.php b/src/Processor/AnonymizerProcessor.php index c250277..a562da5 100644 --- a/src/Processor/AnonymizerProcessor.php +++ b/src/Processor/AnonymizerProcessor.php @@ -5,14 +5,34 @@ namespace Synolia\SyliusGDPRPlugin\Processor; use Doctrine\ORM\EntityManagerInterface; +use Psr\Log\LoggerInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use Synolia\SyliusGDPRPlugin\Provider\AnonymizerInterface; final class AnonymizerProcessor { private const MODULO_FLUSH = 50; - public function __construct(private AnonymizerInterface $anonymizer, private EntityManagerInterface $entityManager) - { + private AnonymizerInterface $anonymizer; + + private EntityManagerInterface $entityManager; + + private TranslatorInterface $translator; + + private LoggerInterface $logger; + + private int $anonymizedEntity = 0; + + public function __construct( + AnonymizerInterface $anonymizer, + EntityManagerInterface $entityManager, + TranslatorInterface $translator, + LoggerInterface $logger, + ) { + $this->anonymizer = $anonymizer; + $this->entityManager = $entityManager; + $this->translator = $translator; + $this->logger = $logger; } public function anonymizeEntities(array $entities, bool $reset = false, int $maxRetries = 50): void @@ -30,10 +50,19 @@ public function anonymizeEntities(array $entities, bool $reset = false, int $max } $this->entityManager->flush(); + + $this->logger->info(sprintf('%d %s', $this->getAnonymizedEntityCount(), $this->translator->trans('sylius.ui.admin.synolia_gdpr.advanced_actions.customer_anonymized_count'))); + } + + public function getAnonymizedEntityCount(): int + { + return $this->anonymizedEntity; } private function anonymizeEntity(Object $entity, bool $reset = false, int $maxRetries = 50): void { $this->anonymizer->anonymize($entity, $reset, $maxRetries); + + ++$this->anonymizedEntity; } } diff --git a/src/Provider/Anonymizer.php b/src/Provider/Anonymizer.php index 2567f20..9b9982e 100644 --- a/src/Provider/Anonymizer.php +++ b/src/Provider/Anonymizer.php @@ -81,6 +81,10 @@ public function anonymize(Object $entity, bool $reset = false, int $maxRetries = if ($this->isSubclass($entity, $className, $propertyName)) { $getter = 'get' . ucfirst($propertyName); + if ($entity->$getter() instanceof \DateTime && $attributeMetaData instanceof AttributeMetaData) { + $this->anonymizeProcess($entity, $reset, $maxRetries, $className, $propertyName, $attributeMetaData); + } + $this->anonymize($entity->$getter(), $reset, $maxRetries); continue; diff --git a/src/Resources/config/mappings/AddressLogEntry.yaml b/src/Resources/config/mappings/AddressLogEntry.yaml index f2683ec..15d8bd5 100644 --- a/src/Resources/config/mappings/AddressLogEntry.yaml +++ b/src/Resources/config/mappings/AddressLogEntry.yaml @@ -1,8 +1,6 @@ Sylius\Component\Addressing\Model\AddressLogEntry: properties: - loggedAt: - faker: dateTime data: value: ['anonymized-details'] objectId: - value: 'anonymized' \ No newline at end of file + value: 'anonymized' diff --git a/src/Resources/config/mappings/ShopUser.yaml b/src/Resources/config/mappings/ShopUser.yaml index c43f9a2..f82b4db 100644 --- a/src/Resources/config/mappings/ShopUser.yaml +++ b/src/Resources/config/mappings/ShopUser.yaml @@ -12,7 +12,8 @@ Sylius\Component\Core\Model\ShopUser: faker: sha256 prefix: 'anonymized-' lastLogin: - faker: dateTime + faker: dateTimeBetween + args: ['+100 years', '+101 years'] emailVerificationToken: value: null passwordResetToken: diff --git a/src/Resources/translations/messages.en.yaml b/src/Resources/translations/messages.en.yaml index 0a67cc5..fbb57e0 100644 --- a/src/Resources/translations/messages.en.yaml +++ b/src/Resources/translations/messages.en.yaml @@ -8,6 +8,7 @@ sylius: gdpr_title: GDPR advanced_actions: title: Advanced actions + customer_anonymized_count: customers anonymized. anonymize_customers_not_logged_before: label: Anonymize customers not logged before anonymize_customer_without_any_orders: diff --git a/src/Resources/translations/messages.fr.yaml b/src/Resources/translations/messages.fr.yaml index 22f40f2..0688717 100644 --- a/src/Resources/translations/messages.fr.yaml +++ b/src/Resources/translations/messages.fr.yaml @@ -8,7 +8,8 @@ sylius: gdpr_title: RGPD advanced_actions: title: Actions avancées - anonymize_customer_not_logged_before: + customer_anonymized_count: client anonymisé. + anonymize_customers_not_logged_before: label: Anonymiser tous les utlisateurs non connectés avant anonymize_customer_without_any_orders: label: Anonymiser tous les utilisateurs n'ayant jamais créé de commande avant diff --git a/tests/PHPUnit/Processor/AdvancedActions/AnonymizeCustomerNotLoggedBeforeProcessorTest.php b/tests/PHPUnit/Processor/AdvancedActions/AnonymizeCustomerNotLoggedBeforeProcessorTest.php index bd85aa0..93ac278 100644 --- a/tests/PHPUnit/Processor/AdvancedActions/AnonymizeCustomerNotLoggedBeforeProcessorTest.php +++ b/tests/PHPUnit/Processor/AdvancedActions/AnonymizeCustomerNotLoggedBeforeProcessorTest.php @@ -10,9 +10,12 @@ use Symfony\Component\Form\Forms; use Synolia\SyliusGDPRPlugin\Form\Type\Actions\AnonymizeCustomersNotLoggedBeforeType; use Synolia\SyliusGDPRPlugin\Processor\AdvancedActions\CompositeAdvancedActionsFormDataProcessor; +use Tests\Synolia\SyliusGDPRPlugin\PHPUnit\Processor\WithSessionTrait; class AnonymizeCustomerNotLoggedBeforeProcessorTest extends KernelTestCase { + use WithSessionTrait; + private ?EntityManagerInterface $manager = null; protected function setUp(): void @@ -32,6 +35,8 @@ protected function tearDown(): void public function testAnonymizeCustomers(): void { + $this->createSession(); + /** @var array $shopUsers */ $shopUsers = static::getContainer()->get('sylius.repository.shop_user')->findAll(); $shopUsers[0]->setLastLogin(new \DateTime()); diff --git a/tests/PHPUnit/Processor/AdvancedActions/AnonymizeCustomerWithoutAnyOrdersBeforeProcessorTest.php b/tests/PHPUnit/Processor/AdvancedActions/AnonymizeCustomerWithoutAnyOrdersBeforeProcessorTest.php index 0c34f4a..d1483d9 100644 --- a/tests/PHPUnit/Processor/AdvancedActions/AnonymizeCustomerWithoutAnyOrdersBeforeProcessorTest.php +++ b/tests/PHPUnit/Processor/AdvancedActions/AnonymizeCustomerWithoutAnyOrdersBeforeProcessorTest.php @@ -11,9 +11,12 @@ use Symfony\Component\Form\Forms; use Synolia\SyliusGDPRPlugin\Form\Type\Actions\AnonymizeCustomersWithoutAnyOrdersBeforeType; use Synolia\SyliusGDPRPlugin\Processor\AdvancedActions\CompositeAdvancedActionsFormDataProcessor; +use Tests\Synolia\SyliusGDPRPlugin\PHPUnit\Processor\WithSessionTrait; class AnonymizeCustomerWithoutAnyOrdersBeforeProcessorTest extends KernelTestCase { + use WithSessionTrait; + private ?EntityManagerInterface $manager = null; protected function setUp(): void @@ -33,6 +36,8 @@ protected function tearDown(): void public function testAnonymizeCustomers(): void { + $this->createSession(); + /** @var array $customers */ $customers = static::getContainer()->get('sylius.repository.customer')->findAll(); $customers[0]->setCreatedAt(new \DateTime()); diff --git a/tests/PHPUnit/Processor/WithSessionTrait.php b/tests/PHPUnit/Processor/WithSessionTrait.php new file mode 100644 index 0000000..9c17d76 --- /dev/null +++ b/tests/PHPUnit/Processor/WithSessionTrait.php @@ -0,0 +1,22 @@ +get(RequestStack::class); + $request = Request::createFromGlobals(); + $request->setSession(new Session(new MockArraySessionStorage())); + $requestStack->push($request); + } +}