diff --git a/Api/Data/EraseCustomerInterface.php b/Api/Data/EraseCustomerInterface.php index f1cfe70..28366ea 100644 --- a/Api/Data/EraseCustomerInterface.php +++ b/Api/Data/EraseCustomerInterface.php @@ -23,6 +23,7 @@ interface EraseCustomerInterface extends ExtensibleDataInterface public const SCHEDULED_AT = 'scheduled_at'; public const STATE = 'state'; public const STATUS = 'status'; + public const MESSAGE = 'message'; public const ERASED_AT = 'erased_at'; /**#@-*/ @@ -118,6 +119,21 @@ public function getStatus(): string; */ public function setStatus(string $status): EraseCustomerInterface; + /** + * Retrieve the error message + * + * @return string|null + */ + public function getMessage(): ?string; + + /** + * Set the error message + * + * @param string|null $message + * @return \Opengento\Gdpr\Api\Data\EraseCustomerInterface + */ + public function setMessage(?string $message): EraseCustomerInterface; + /** * Retrieve the erased at if it exists * diff --git a/Controller/Adminhtml/Privacy/MassErase.php b/Controller/Adminhtml/Privacy/MassErase.php new file mode 100644 index 0000000..d698ba7 --- /dev/null +++ b/Controller/Adminhtml/Privacy/MassErase.php @@ -0,0 +1,78 @@ +eraseCustomerManagement = $eraseCustomerManagement; + parent::__construct($context, $filter, $collectionFactory); + } + + /** + * @inheritdoc + */ + protected function massAction(AbstractCollection $collection) + { + $customerErased = 0; + + foreach ($collection->getAllIds() as $customerId) { + try { + $this->eraseCustomerManagement->process($this->eraseCustomerManagement->create((int) $customerId)); + $customerErased++; + } catch (LocalizedException $e) { + $this->messageManager->addErrorMessage( + new Phrase('Customer with id "%1": %2', [$customerId, $e->getMessage()]) + ); + } catch (\Exception $e) { + $this->messageManager->addExceptionMessage($e, new Phrase('An error occurred on the server.')); + } + } + + if ($customerErased) { + $this->messageManager->addSuccessMessage( + new Phrase('A total of %1 record(s) were erased.', [$customerErased]) + ); + } + + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + $resultRedirect->setPath('customer/index/index'); + + return $resultRedirect; + } +} diff --git a/Model/EraseCustomer.php b/Model/EraseCustomer.php index abca122..c265676 100755 --- a/Model/EraseCustomer.php +++ b/Model/EraseCustomer.php @@ -106,6 +106,22 @@ public function setStatus(string $status): EraseCustomerInterface return $this->setData(self::STATUS, $status); } + /** + * @inheritdoc + */ + public function getMessage(): ?string + { + return $this->_getData(self::MESSAGE) === null ? null : (string) $this->_getData(self::MESSAGE); + } + + /** + * @inheritdoc + */ + public function setMessage(?string $message): EraseCustomerInterface + { + return $this->setData(self::MESSAGE, $message); + } + /** * @inheritdoc */ diff --git a/Model/EraseCustomerChecker.php b/Model/EraseCustomerChecker.php index 8273f22..9f88f69 100644 --- a/Model/EraseCustomerChecker.php +++ b/Model/EraseCustomerChecker.php @@ -91,6 +91,6 @@ public function canProcess(int $customerId): bool && $entity->getStatus() === EraseCustomerInterface::STATUS_READY) || ($entity->getState() === EraseCustomerInterface::STATE_PROCESSING && $entity->getStatus() === EraseCustomerInterface::STATUS_FAILED)) - && $this->customerChecker->hasPendingOrders($entity->getCustomerId()); + && !$this->customerChecker->hasPendingOrders($entity->getCustomerId()); } } diff --git a/Model/EraseCustomerManagement.php b/Model/EraseCustomerManagement.php index 0d33ff7..5e368ad 100644 --- a/Model/EraseCustomerManagement.php +++ b/Model/EraseCustomerManagement.php @@ -114,6 +114,7 @@ public function cancel(int $customerId): bool /** * @inheritdoc + * @throws \Exception */ public function process(EraseCustomerInterface $entity): EraseCustomerInterface { @@ -128,14 +129,47 @@ public function process(EraseCustomerInterface $entity): EraseCustomerInterface $entity = $this->eraseCustomerRepository->save($entity); try { - $this->eraseManagement->erase($entity->getCustomerId()); - $entity->setState(EraseCustomerInterface::STATE_COMPLETE); - $entity->setStatus(EraseCustomerInterface::STATUS_SUCCEED); - $entity->setErasedAt($this->localeDate->gmtDate()); + if ($this->eraseManagement->erase($entity->getCustomerId())) { + return $this->success($entity); + } + + return $this->fail($entity); } catch (\Exception $e) { - $entity->setState(EraseCustomerInterface::STATE_PROCESSING); - $entity->setStatus(EraseCustomerInterface::STATUS_FAILED); + $this->fail($entity, $e->getMessage()); + throw $e; } + } + + /** + * Erasure has succeeded + * + * @param \Opengento\Gdpr\Api\Data\EraseCustomerInterface $entity + * @return \Opengento\Gdpr\Api\Data\EraseCustomerInterface + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + private function success(EraseCustomerInterface $entity): EraseCustomerInterface + { + $entity->setState(EraseCustomerInterface::STATE_COMPLETE); + $entity->setStatus(EraseCustomerInterface::STATUS_SUCCEED); + $entity->setErasedAt($this->localeDate->gmtDate()); + $entity->setMessage(null); + + return $this->eraseCustomerRepository->save($entity); + } + + /** + * Erasure has failed + * + * @param \Opengento\Gdpr\Api\Data\EraseCustomerInterface $entity + * @param string|null $message + * @return \Opengento\Gdpr\Api\Data\EraseCustomerInterface + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + private function fail(EraseCustomerInterface $entity, ?string $message = null): EraseCustomerInterface + { + $entity->setState(EraseCustomerInterface::STATE_PROCESSING); + $entity->setStatus(EraseCustomerInterface::STATUS_FAILED); + $entity->setMessage($message); return $this->eraseCustomerRepository->save($entity); } diff --git a/Model/Newsletter/Subscriber.php b/Model/Newsletter/Subscriber.php index f3850cc..d0186a6 100644 --- a/Model/Newsletter/Subscriber.php +++ b/Model/Newsletter/Subscriber.php @@ -7,10 +7,13 @@ namespace Opengento\Gdpr\Model\Newsletter; +use \Magento\Newsletter\Model\Subscriber as SubscriberModel; use Magento\Newsletter\Model\SubscriberFactory; /** * `\Opengento\Gdpr\Model\Newsletter\Subscriber` class is the final state of `\Magento\Newsletter\Model\Subscriber`. + * + * @method SubscriberModel loadByCustomerId(int $customerId) */ final class Subscriber { @@ -30,6 +33,16 @@ public function __construct( $this->subscriber = $subscriberFactory->create(['data' => $data]); } + /** + * Retrieve the real subscriber subject + * + * @return \Magento\Newsletter\Model\Subscriber + */ + public function getRealSubscriber(): SubscriberModel + { + return $this->subscriber; + } + /** * @inheritdoc */ diff --git a/Service/Anonymize/Processor/SubscriberDataProcessor.php b/Service/Anonymize/Processor/SubscriberDataProcessor.php index 96f5d86..21afcbf 100644 --- a/Service/Anonymize/Processor/SubscriberDataProcessor.php +++ b/Service/Anonymize/Processor/SubscriberDataProcessor.php @@ -53,10 +53,11 @@ public function __construct( */ public function execute(int $customerId): bool { - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ + /** @var \Opengento\Gdpr\Model\Newsletter\Subscriber $subscriber */ $subscriber = $this->subscriberFactory->create(); $subscriber->loadByCustomerId($customerId); - $this->subscriberResourceModel->save($this->anonymizer->anonymize($subscriber)); + $this->anonymizer->anonymize($subscriber); + $this->subscriberResourceModel->save($subscriber->getRealSubscriber()); return true; } diff --git a/Service/Delete/Processor/SubscriberDataProcessor.php b/Service/Delete/Processor/SubscriberDataProcessor.php index 579460f..21077ab 100644 --- a/Service/Delete/Processor/SubscriberDataProcessor.php +++ b/Service/Delete/Processor/SubscriberDataProcessor.php @@ -8,7 +8,7 @@ namespace Opengento\Gdpr\Service\Delete\Processor; use Magento\Newsletter\Model\ResourceModel\Subscriber as ResourceSubscriber; -use Opengento\Gdpr\Model\Newsletter\SubscriberFactory; +use Magento\Newsletter\Model\SubscriberFactory; use Opengento\Gdpr\Service\Delete\ProcessorInterface; /** @@ -17,7 +17,7 @@ final class SubscriberDataProcessor implements ProcessorInterface { /** - * @var \Opengento\Gdpr\Model\Newsletter\SubscriberFactory + * @var \Magento\Newsletter\Model\SubscriberFactory */ private $subscriberFactory; @@ -27,7 +27,7 @@ final class SubscriberDataProcessor implements ProcessorInterface private $subscriberResourceModel; /** - * @param \Opengento\Gdpr\Model\Newsletter\SubscriberFactory $subscriberFactory + * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory * @param \Magento\Newsletter\Model\ResourceModel\Subscriber $subscriberResourceModel */ public function __construct( diff --git a/Setup/InstallSchema.php b/Setup/InstallSchema.php index a20e356..2cc7283 100755 --- a/Setup/InstallSchema.php +++ b/Setup/InstallSchema.php @@ -79,6 +79,13 @@ private function createEraseCustomerTable(SchemaSetupInterface $setup): bool ['nullable' => false], 'Status' ) + ->addColumn( + EraseCustomerInterface::MESSAGE, + Table::TYPE_TEXT, + 255, + ['nullable' => true], + 'Message' + ) ->addColumn( EraseCustomerInterface::ERASED_AT, Table::TYPE_TIMESTAMP, @@ -105,7 +112,7 @@ private function createEraseCustomerTable(SchemaSetupInterface $setup): bool EraseCustomerInterface::CUSTOMER_ID, $setup->getTable('customer_entity'), 'entity_id', - Table::ACTION_NO_ACTION + Table::ACTION_CASCADE ) ->setComment('Customer Erase Scheduler'); diff --git a/TODO.md b/TODO.md index 55d161f..0449612 100644 --- a/TODO.md +++ b/TODO.md @@ -23,8 +23,7 @@ Area: - Multiple export in command line result in filename expanded at each iteration ## Anonymizer - -- Check customer only closed/completed orders + - Payment Data - Invitation Data diff --git a/etc/adminhtml/routes.xml b/etc/adminhtml/routes.xml new file mode 100644 index 0000000..296cf02 --- /dev/null +++ b/etc/adminhtml/routes.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/view/adminhtml/ui_component/customer_listing.xml b/view/adminhtml/ui_component/customer_listing.xml new file mode 100644 index 0000000..db12ba0 --- /dev/null +++ b/view/adminhtml/ui_component/customer_listing.xml @@ -0,0 +1,24 @@ + + ++ + + + + + Are you sure you want to erase the selected customers? + Erase items + + + erase + + + + + +