diff --git a/Api/CustomerManagementInterface.php b/Api/CustomerManagementInterface.php new file mode 100644 index 0000000..55cf3ec --- /dev/null +++ b/Api/CustomerManagementInterface.php @@ -0,0 +1,74 @@ +getUrl('privacy/delete/delete'); - } + const ATTRIBUTE_IS_ANONYMIZED = 'is_anonymized'; } diff --git a/Api/Data/ReasonInterface.php b/Api/Data/ReasonInterface.php new file mode 100644 index 0000000..8dda6e3 --- /dev/null +++ b/Api/Data/ReasonInterface.php @@ -0,0 +1,77 @@ +_init(ResourceModel\CronSchedule::class); - } + public function getExportProcessors(); } diff --git a/Api/ReasonRepositoryInterface.php b/Api/ReasonRepositoryInterface.php new file mode 100644 index 0000000..5f4d7ac --- /dev/null +++ b/Api/ReasonRepositoryInterface.php @@ -0,0 +1,79 @@ +helper = $helper; - $this->orderConfig = $orderConfig; - $this->accountData = $accountData; - } - - /** - * Get action controller url - * - * @return string - */ - public function getAction() - { - return $this->getUrl('privacy/delete/delete'); - } - - /** - * Get privacy settings url - * - * @return string - */ - public function getSettingsUrl() - { - return $this->getUrl('privacy/settings'); - } - - /** - * Get Information Page url from configuration file - * - * @return string - */ - public function getInformationPageUrl() - { - return $this->getUrl($this->helper->getInformationPage()); - } - - /** - * Check if customer has orders. - * - * @return bool - */ - public function hasOrders() - { - return $this->accountData->hasOrders(); - } - - /** - * Check if customer is deleting his account. - * - * @return bool - */ - public function isAccountToBeDeleted() - { - return $this->accountData->isAccountToBeDeleted(); - } -} diff --git a/Block/Account/Settings.php b/Block/Account/Settings.php deleted file mode 100755 index 03714d5..0000000 --- a/Block/Account/Settings.php +++ /dev/null @@ -1,85 +0,0 @@ -getUrl('privacy/delete'); - } - - /** - * Get undo delete page url. - * - * @return string - */ - public function getUndoDeletePageURL() - { - return $this->getUrl('privacy/delete/undodelete'); - } - - /** - * Get Anonymise page url. - */ - public function getAnonymisePageURL() - { - return $this->getUrl('privacy/anonymise'); - } - - /** - * Get export controller. - * - * @return string - */ - public function getExportAction() - { - return $this->getUrl('privacy/export/export'); - } - - /** - * Get export page url. - * - * @return string - */ - public function getExportPageUrl() - { - return $this->getUrl('privacy/export'); - } - - /** - * Check if account should be anonymized instead of deleted. - * - * @return bool - */ - public function shouldAnonymize() - { - return ($this->helper->getDeletionSchema() === Schema::ANONYMIZE || $this->hasOrders()) && - $this->helper->isAnonymizationMessageEnabled(); - } -} diff --git a/Block/Customer/Privacy/Delete.php b/Block/Customer/Privacy/Delete.php new file mode 100644 index 0000000..84cec33 --- /dev/null +++ b/Block/Customer/Privacy/Delete.php @@ -0,0 +1,82 @@ +privacyHelper = $privacyHelper; + } + + /** + * Get action controller url. + * + * @return string + */ + public function getAction() + { + return $this->getUrl('privacy/settings/deletePost'); + } + + /** + * Get privacy settings url. + * + * @return string + */ + public function getSettingsUrl() + { + return $this->getUrl('privacy/settings'); + } + + /** + * Get privacy helper. + * + * @return PrivacyHelper + */ + public function getPrivacyHelper() + { + return $this->privacyHelper; + } +} diff --git a/Block/Customer/Privacy/Settings.php b/Block/Customer/Privacy/Settings.php new file mode 100644 index 0000000..f466411 --- /dev/null +++ b/Block/Customer/Privacy/Settings.php @@ -0,0 +1,153 @@ +customerManagement = $customerManagement; + $this->privacyHelper = $privacyHelper; + $this->customerSession = $customerSession; + } + + /** + * Get privacy helper. + * + * @return PrivacyHelper + */ + public function getPrivacyHelper() + { + return $this->privacyHelper; + } + + /** + * Get delete page url. + * + * @return string + */ + public function getDeletePageUrl() + { + return $this->getUrl('privacy/settings/delete'); + } + + /** + * Get undo delete page url. + * + * @return string + */ + public function getUndoDeletePageUrl() + { + return $this->getUrl('privacy/settings/undodelete'); + } + + /** + * Get export page url. + * + * @return string + */ + public function getExportPageUrl() + { + return $this->getUrl('privacy/settings/export'); + } + + /** + * Check if account should be anonymized instead of deleted. + * + * @return bool + */ + public function shouldAnonymize() + { + return $this->privacyHelper->isAnonymizationMessageEnabled() && + ($this->privacyHelper->getDeletionSchema() === Schema::ANONYMIZE || $this->hasOrders()); + } + + /** + * Check if customer has orders. + * + * @return bool + */ + public function hasOrders() + { + if (!$customer = $this->customerSession->getCustomerData()) { + return false; + } + + return $this->customerManagement->hasOrders($customer); + } + + /** + * Check if account is to be deleted. + * + * @return bool + */ + public function isAccountToBeDeleted() + { + if (!$customer = $this->customerSession->getCustomerData()) { + return false; + } + + return $this->customerManagement->isCustomerToBeDeleted($customer); + } +} diff --git a/Block/Messages/PrivacyMessagePopup.php b/Block/Customer/PrivacyPopup.php similarity index 61% rename from Block/Messages/PrivacyMessagePopup.php rename to Block/Customer/PrivacyPopup.php index 1cd6cde..de61ebc 100644 --- a/Block/Messages/PrivacyMessagePopup.php +++ b/Block/Customer/PrivacyPopup.php @@ -14,45 +14,52 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Block\Messages; +declare(strict_types=1); -use Flurrybox\EnhancedPrivacy\Helper\Data; +namespace Flurrybox\EnhancedPrivacy\Block\Customer; + +use Flurrybox\EnhancedPrivacy\Helper\Data as PrivacyHelper; use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Framework\View\Element\Template; /** - * Privacy popup message block. + * Privacy policy popup block. */ -class PrivacyMessagePopup extends Template +class PrivacyPopup extends Template { + /** + * @var string + */ + protected $_template = 'Flurrybox_EnhancedPrivacy::customer/privacy_popup.phtml'; + /** * @var CookieManagerInterface */ protected $cookieManager; /** - * @var Data + * @var PrivacyHelper */ - protected $helper; + protected $privacyHelper; /** * PrivacyMessagePopup constructor. * * @param Template\Context $context * @param CookieManagerInterface $cookieManager - * @param Data $helper + * @param PrivacyHelper $privacyHelper * @param array $data */ public function __construct( Template\Context $context, CookieManagerInterface $cookieManager, - Data $helper, + PrivacyHelper $privacyHelper, array $data = [] ) { parent::__construct($context, $data); $this->cookieManager = $cookieManager; - $this->helper = $helper; + $this->privacyHelper = $privacyHelper; } /** @@ -63,8 +70,8 @@ public function __construct( protected function _toHtml() { if ( - !$this->helper->isModuleEnabled() || - !$this->helper->isPopupNotificationEnabled() + !$this->privacyHelper->isModuleEnabled() && + !$this->privacyHelper->isPopupNotificationEnabled() ) { return ''; } @@ -79,10 +86,10 @@ protected function _toHtml() */ public function getJsLayout() { - $this->jsLayout['components']['enhanced-privacy-cookie-policy']['config'] = [ - 'cookieName' => Data::COOKIE_COOKIES_POLICY, - 'learnMore' => $this->getUrl($this->helper->getInformationPage()), - 'notificationText' => $this->helper->getPopupNotificationText() + $this->jsLayout['components']['enhancedprivacy-cookie-policy']['config'] = [ + 'cookieName' => PrivacyHelper::COOKIE_COOKIES_POLICY, + 'learnMore' => $this->getUrl($this->privacyHelper->getInformationPage()), + 'notificationText' => $this->privacyHelper->getPopupNotificationText() ]; return json_encode($this->jsLayout); diff --git a/Controller/Adminhtml/Reasons/Index.php b/Controller/Adminhtml/Reasons/Index.php new file mode 100644 index 0000000..e290fb6 --- /dev/null +++ b/Controller/Adminhtml/Reasons/Index.php @@ -0,0 +1,44 @@ +resultFactory->create(ResultFactory::TYPE_PAGE); + } +} diff --git a/Controller/Adminhtml/Reasons/MassDelete.php b/Controller/Adminhtml/Reasons/MassDelete.php new file mode 100644 index 0000000..6c3bddf --- /dev/null +++ b/Controller/Adminhtml/Reasons/MassDelete.php @@ -0,0 +1,96 @@ +filter = $filter; + $this->reasonCollectionFactory = $reasonCollectionFactory; + $this->reasonRepository = $reasonRepository; + } + + /** + * Execute action. + * + * @return \Magento\Framework\Controller\ResultInterface|ResponseInterface + */ + public function execute() + { + try { + $collection = $this->filter->getCollection($this->reasonCollectionFactory->create()); + + foreach ($collection->getItems() as $item) { + $this->reasonRepository->delete($item); + } + + $this->messageManager->addSuccessMessage(__('Deletion reasons successfully deleted!')); + } catch (Exception $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + } + + return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('*/*/index'); + } +} diff --git a/Controller/Delete/Delete.php b/Controller/Delete/Delete.php deleted file mode 100755 index 9694f3c..0000000 --- a/Controller/Delete/Delete.php +++ /dev/null @@ -1,253 +0,0 @@ -formKeyValidator = $formKeyValidator; - $this->customerRepository = $customerRepository; - $this->orderConfig = $orderConfig; - $this->authentication = $authentication; - $this->dateTime = $dateTime; - $this->session = $session; - $this->helper = $helper; - $this->accountData = $accountData; - $this->scheduleFactory = $scheduleFactory; - } - - /** - * Dispatch controller. - * - * @param RequestInterface $request - * - * @return \Magento\Framework\App\ResponseInterface - * @throws \Magento\Framework\Exception\NotFoundException - */ - public function dispatch(RequestInterface $request) - { - if (!$this->session->authenticate()) { - $this->_actionFlag->set('', 'no-dispatch', true); - } - - if ( - !$this->helper->isModuleEnabled() || - !$this->helper->isAccountDeletionEnabled() || - $this->accountData->isAccountToBeDeleted() - ) { - $this->_forward('no_route'); - } - - return parent::dispatch($request); - } - - /** - * Execute controller. - * - * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @throws \Magento\Framework\Exception\SessionException - */ - public function execute() - { - /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultRedirectFactory->create(); - - $validFormKey = $this->formKeyValidator->validate($this->getRequest()); - if ($this->getRequest()->isPost() && !$validFormKey) { - return $resultRedirect->setPath('privacy/settings'); - } - - $customerId = $this->session->getCustomerId(); - $currentCustomerDataObject = $this->getCustomerDataObject($customerId); - - try { - $this->authenticate($currentCustomerDataObject); - - /** @var \Flurrybox\EnhancedPrivacy\Model\CronSchedule $schedule */ - $schedule = $this->scheduleFactory->create() - ->setData( - 'scheduled_at', - date('Y-m-d H:i:s', $this->dateTime->gmtTimestamp() + $this->helper->getDeletionTime()) - ) - ->setData('customer_id', $customerId) - ->setData('reason', $this->getRequest()->getPost('reason')); - - switch ($this->helper->getDeletionSchema()) { - case Schema::DELETE: - $schedule->setData('type', Data::SCHEDULE_TYPE_DELETE); - break; - - case Schema::ANONYMIZE: - $schedule->setData('type', Data::SCHEDULE_TYPE_ANONYMIZE); - break; - - case Schema::DELETE_ANONYMIZE: - $schedule->setData( - 'type', - $this->accountData->hasOrders() ? Data::SCHEDULE_TYPE_ANONYMIZE : Data::SCHEDULE_TYPE_DELETE - ); - break; - } - - $schedule->getResource()->save($schedule); - - $this->messageManager->addWarningMessage(__($this->helper->getSuccessMessage())); - - return $resultRedirect->setPath('privacy/settings'); - } catch (InvalidEmailOrPasswordException $e) { - $this->messageManager->addErrorMessage($e->getMessage()); - } catch (UserLockedException $e) { - $this->session->logout(); - $this->session->start(); - $this->messageManager - ->addErrorMessage(__('You did not sign in correctly or your account is temporarily disabled.')); - - return $resultRedirect->setPath('customer/account/login'); - } catch (\Exception $e) { - $this->messageManager->addErrorMessage(__('Something went wrong, please try again later!')); - } - - return $resultRedirect->setPath('privacy/settings'); - } - - /** - * Get customer data object - * - * @param int $customerId - * - * @return CustomerInterface - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - private function getCustomerDataObject($customerId) - { - return $this->customerRepository->getById($customerId); - } - - /** - * Authenticate user. - * - * @param CustomerInterface $currentCustomerDataObject - * - * @return void - * @throws InvalidEmailOrPasswordException - * @throws \Magento\Framework\Exception\State\UserLockedException - */ - private function authenticate(CustomerInterface $currentCustomerDataObject) - { - try { - $this->authentication - ->authenticate($currentCustomerDataObject->getId(), $this->getRequest()->getPost('password')); - } catch (InvalidEmailOrPasswordException $e) { - throw new InvalidEmailOrPasswordException(__('Password you typed does not match this account.')); - } - } -} diff --git a/Controller/Delete/Index.php b/Controller/Delete/Index.php deleted file mode 100755 index ab2cc26..0000000 --- a/Controller/Delete/Index.php +++ /dev/null @@ -1,105 +0,0 @@ -helper = $helper; - $this->session = $session; - $this->accountData = $accountData; - } - - /** - * Dispatch controller. - * - * @param RequestInterface $request - * - * @return \Magento\Framework\App\ResponseInterface - * @throws \Magento\Framework\Exception\NotFoundException - */ - public function dispatch(RequestInterface $request) - { - if (!$this->session->authenticate()) { - $this->_actionFlag->set('', 'no-dispatch', true); - } - - if ( - !$this->helper->isModuleEnabled() || - !$this->helper->isAccountDeletionEnabled() || - $this->accountData->isAccountToBeDeleted() - ) { - $this->_forward('no_route'); - } - - return parent::dispatch($request); - } - - /** - * Execute controller. - * - * @return void - */ - public function execute() - { - $this->_view->loadLayout(); - - if ($block = $this->_view->getLayout()->getBlock('privacy_delete')) { - $block->setRefererUrl($this->_redirect->getRefererUrl()); - } - - $this->_view->getPage()->getConfig()->getTitle()->set(__('Delete account')); - - $this->_view->renderLayout(); - } -} diff --git a/Controller/Delete/UndoDelete.php b/Controller/Delete/UndoDelete.php deleted file mode 100755 index 5d62fad..0000000 --- a/Controller/Delete/UndoDelete.php +++ /dev/null @@ -1,126 +0,0 @@ -collectionFactory = $collectionFactory; - $this->session = $session; - $this->helper = $helper; - $this->accountData = $accountData; - } - - /** - * Dispatch controller. - * - * @param RequestInterface $request - * - * @return \Magento\Framework\App\ResponseInterface - * @throws \Magento\Framework\Exception\NotFoundException - */ - public function dispatch(RequestInterface $request) - { - if (!$this->session->authenticate()) { - $this->_actionFlag->set('', 'no-dispatch', true); - } - - if ( - !$this->helper->isModuleEnabled() || - !$this->helper->isAccountDeletionEnabled() || - !$this->accountData->isAccountToBeDeleted() - ) { - $this->_forward('no_route'); - } - - return parent::dispatch($request); - } - - /** - * Execute controller. - * - * @return \Magento\Framework\Controller\Result\Redirect - */ - public function execute() - { - /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultRedirectFactory->create(); - - try { - /** @var \Flurrybox\EnhancedPrivacy\Model\CronSchedule $model */ - $model = $this->collectionFactory->create() - ->getItemByColumnValue('customer_id', $this->session->getId()); - - $model->getResource()->delete($model); - - $this->messageManager->addSuccessMessage(__('You canceled your account deletion.')); - } catch (Exception $e) { - $this->messageManager->addWarningMessage(__('Something went wrong, please try again later!')); - } - - return $resultRedirect->setPath('privacy/settings'); - } -} diff --git a/Controller/Export/Export.php b/Controller/Export/Export.php deleted file mode 100755 index d808a02..0000000 --- a/Controller/Export/Export.php +++ /dev/null @@ -1,255 +0,0 @@ -context = $context; - $this->dateTime = $dateTime; - $this->fileFactory = $fileFactory; - $this->file = $file; - $this->csvWriter = $csvWriter; - $this->zip = $zip; - $this->helper = $helper; - $this->customerRepository = $customerRepository; - $this->session = $session; - $this->processors = $processors; - } - - /** - * Dispatch controller. - * - * @param RequestInterface $request - * - * @return \Magento\Framework\App\ResponseInterface - * @throws \Magento\Framework\Exception\NotFoundException - */ - public function dispatch(RequestInterface $request) - { - if (!$this->session->authenticate()) { - $this->_actionFlag->set('', 'no-dispatch', true); - } - - if (!$this->helper->isModuleEnabled() || !$this->helper->isAccountExportEnabled()){ - $this->_forward('no_route'); - } - - return parent::dispatch($request); - } - - /** - * Execute export action. - * - * @return \Magento\Framework\Controller\ResultInterface - * @throws \Exception - */ - public function execute() - { - /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultRedirectFactory->create(); - - try { - $this->downloadZip(); - } catch (\Exception $e) { - $this->messageManager->addErrorMessage(__('Something went wrong. Try again later.')); - - return $resultRedirect->setPath('privacy/export'); - } - - return $this->resultFactory->create(ResultFactory::TYPE_RAW); - } - - /** - * This function download .zip file with customer data. - * - * @return void - * @throws \Exception - * @throws \Magento\Framework\Exception\FileSystemException - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - protected function downloadZip() - { - $customer = $this->customerRepository->getById($this->session->getCustomerId()); - $date = $this->getDateStamp(); - - $zipFileName = 'customer_data_' . $date . '.zip'; - - foreach ($this->processors as $name => $processor) { - if (!$processor instanceof DataExportInterface) { - continue; - } - - $file = $name . '_' . $date . '.csv'; - - $this->createCsv($file, $processor->export($customer)); - $this->zip->pack($file, $zipFileName); - $this->deleteCsv($file); - } - - $this->fileFactory->create( - $zipFileName, - [ - 'type' => 'filename', - 'value' => $zipFileName, - 'rm' => true, - ], - DirectoryList::PUB, - 'zip', - null - ); - } - - /** - * Create .csv file. - * - * @param string $fileName - * @param array $data - * - * @return void - */ - private function createCsv($fileName, $data) - { - if (!$data) { - return null; - } - - $this->csvWriter - ->setEnclosure('"') - ->setDelimiter(',') - ->saveData($fileName, $data); - } - - /** - * Delete .csv file. - * - * @param string $fileName - * @throws \Magento\Framework\Exception\FileSystemException - */ - private function deleteCsv($fileName) - { - if ($this->file->isExists($fileName)) { - $this->file->deleteFile($fileName); - } - } - - /** - * Return current date. - * - * @return false|string - */ - private function getDateStamp() - { - return date('Y-m-d_H-i-s', $this->dateTime->gmtTimestamp()); - } -} diff --git a/Controller/Settings/Delete.php b/Controller/Settings/Delete.php new file mode 100644 index 0000000..9ca7492 --- /dev/null +++ b/Controller/Settings/Delete.php @@ -0,0 +1,107 @@ +privacyHelper = $privacyHelper; + $this->customerSession = $customerSession; + $this->customerManagement = $customerManagement; + } + + /** + * Dispatch controller. + * + * @param RequestInterface $request + * + * @return ResponseInterface + * @throws \Magento\Framework\Exception\NotFoundException + */ + public function dispatch(RequestInterface $request) + { + try { + if ( + !$this->privacyHelper->isModuleEnabled() || + !$this->privacyHelper->isAccountDeletionEnabled() || + $this->customerManagement->isCustomerToBeDeleted($this->customerSession->getCustomerData()) + ) { + $this->_forward('noroute'); + } + } catch (Exception $e) { + $this->_forward('noroute'); + } + + return parent::dispatch($request); + } + + /** + * Execute action. + * + * @return \Magento\Framework\Controller\ResultInterface|ResponseInterface + */ + public function execute() + { + return $this->resultFactory->create(ResultFactory::TYPE_PAGE); + } +} diff --git a/Controller/Settings/DeletePost.php b/Controller/Settings/DeletePost.php new file mode 100644 index 0000000..eb0fb91 --- /dev/null +++ b/Controller/Settings/DeletePost.php @@ -0,0 +1,213 @@ +privacyHelper = $privacyHelper; + $this->customerSession = $customerSession; + $this->customerManagement = $customerManagement; + $this->formKeyValidator = $formKeyValidator; + $this->authentication = $authentication; + $this->scheduleFactory = $scheduleFactory; + $this->scheduleRepository = $scheduleRepository; + $this->reasonFactory = $reasonFactory; + $this->reasonRepository = $reasonRepository; + $this->dateTime = $dateTime; + } + + /** + * Dispatch controller. + * + * @param RequestInterface $request + * + * @return \Magento\Framework\App\ResponseInterface + * @throws \Magento\Framework\Exception\NotFoundException + */ + public function dispatch(RequestInterface $request) + { + try { + if ( + !$this->privacyHelper->isModuleEnabled() || + !$this->privacyHelper->isAccountDeletionEnabled() || + $this->customerManagement->isCustomerToBeDeleted($this->customerSession->getCustomerData()) + ) { + $this->_forward('noroute'); + } + } catch (Exception $e) { + $this->_forward('noroute'); + } + + return parent::dispatch($request); + } + + /** + * Execute action. + * + * @return \Magento\Framework\Controller\ResultInterface|ResponseInterface + * @throws \Magento\Framework\Exception\SessionException + */ + public function execute() + { + if ( + !$this->getRequest()->isPost() && + $this->formKeyValidator->validate($this->getRequest()) + ) { + return $this->resultRedirectFactory->create()->setPath('privacy/settings'); + } + + try { + $customer = $this->customerSession->getCustomerData(); + + $this->authentication->authenticate($customer->getId(), $this->getRequest()->getParam('password')); + + $reason = $this->reasonFactory->create(); + $reason->setReason($this->getRequest()->getParam('reason')); + $this->reasonRepository->save($reason); + + $schedule = $this->scheduleFactory->create(); + $schedule + ->setScheduledAt( + date('Y-m-d H:i:s', $this->dateTime->gmtTimestamp() + $this->privacyHelper->getDeletionTime()) + ) + ->setCustomerId((int) $customer->getId()) + ->setReasonId((int) $reason->getId()) + ->setType($this->privacyHelper->getDeletionType($customer)); + + $this->_eventManager->dispatch('enhancedprivacy_submit_delete_request', ['schedule' => $schedule]); + + $this->scheduleRepository->save($schedule); + + $this->messageManager->addWarningMessage($this->privacyHelper->getSuccessMessage()); + } catch (InvalidEmailOrPasswordException $e) { + $this->messageManager->addErrorMessage(__('Password you typed does not match this account!')); + } catch (UserLockedException $e) { + $this->customerSession->logout(); + $this->customerSession->start(); + + $this->messageManager + ->addErrorMessage(__('You did not sign in correctly or your account is temporarily disabled.')); + } catch (Exception $e) { + $this->messageManager->addErrorMessage(__('Something went wrong, please try again later!')); + } + + return $this->resultRedirectFactory->create()->setPath('privacy/settings'); + } +} diff --git a/Controller/Settings/Export.php b/Controller/Settings/Export.php new file mode 100644 index 0000000..3c54144 --- /dev/null +++ b/Controller/Settings/Export.php @@ -0,0 +1,238 @@ +privacyHelper = $privacyHelper; + $this->customerSession = $customerSession; + $this->processors = $processors; + $this->fileFactory = $fileFactory; + $this->dateTime = $dateTime; + $this->csvWriter = $csvWriter; + $this->zip = $zip; + $this->file = $file; + } + + /** + * Dispatch controller. + * + * @param RequestInterface $request + * + * @return \Magento\Framework\App\ResponseInterface + * @throws \Magento\Framework\Exception\NotFoundException + */ + public function dispatch(RequestInterface $request) + { + if ( + !$this->privacyHelper->isModuleEnabled() && + !$this->privacyHelper->isAccountExportEnabled() + ) { + $this->_forward('noroute'); + } + + return parent::dispatch($request); + } + + /** + * Execute action. + * + * @return \Magento\Framework\Controller\ResultInterface|ResponseInterface + */ + public function execute() + { + try { + $customer = $this->customerSession->getCustomerData(); + $zipFileName = $this->getArchiveName($customer); + + foreach ($this->processors->getExportProcessors() as $name => $processor) { + $file = $this->getFileName($customer, $name); + + $this->createCsv($file, $processor->export($customer)); + $this->zip->pack($file, $zipFileName); + $this->deleteCsv($file); + } + + return $this->fileFactory->create( + $zipFileName, + [ + 'type' => 'filename', + 'value' => $zipFileName, + 'rm' => true + ], + DirectoryList::PUB, + 'zip', + null + ); + } catch (Exception $e) { + $this->messageManager->addErrorMessage(__('Something went wrong, please try again later.')); + } + + return $this->resultRedirectFactory->create()->setPath('privacy/settings'); + } + + /** + * Get ZIP archive file name. + * + * @param CustomerInterface $customer + * + * @return string + */ + protected function getArchiveName(CustomerInterface $customer) + { + return sprintf( + 'customer_data_%d_%s.zip', + $customer->getId(), + date('Y-m-d_H-i-s', $this->dateTime->gmtTimestamp()) + ); + } + + /** + * Get CSV file name. + * + * @param CustomerInterface $customer + * @param string $name + * + * @return string + */ + protected function getFileName(CustomerInterface $customer, string $name) + { + return sprintf('%s_%d_%s.csv', $name, $customer->getId(), date('Y-m-d_H-i-s', $this->dateTime->gmtTimestamp())); + } + + /** + * Create .csv file. + * + * @param string $fileName + * @param array|string|null $data + * + * @return void + */ + protected function createCsv(string $fileName, $data) + { + if (!$data) { + return null; + } + + $this->csvWriter + ->setEnclosure('"') + ->setDelimiter(',') + ->saveData($fileName, $data); + } + + /** + * Delete .csv file. + * + * @param string $fileName + * + * @return void + * @throws \Magento\Framework\Exception\FileSystemException + */ + protected function deleteCsv(string $fileName) + { + if ($this->file->isExists($fileName)) { + $this->file->deleteFile($fileName); + } + } +} diff --git a/Controller/Settings/Index.php b/Controller/Settings/Index.php old mode 100755 new mode 100644 index 923e09a..1bec79f --- a/Controller/Settings/Index.php +++ b/Controller/Settings/Index.php @@ -14,42 +14,49 @@ * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Flurrybox\EnhancedPrivacy\Controller\Settings; -use Flurrybox\EnhancedPrivacy\Helper\Data; -use Magento\Customer\Model\Session; -use Magento\Framework\App\Action\Action; +use Flurrybox\EnhancedPrivacy\Helper\Data as PrivacyHelper; +use Magento\Customer\Controller\AbstractAccount; +use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\App\Action\Context; use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultFactory; /** * Settings controller. */ -class Index extends Action +class Index extends AbstractAccount { /** - * @var Data + * @var PrivacyHelper */ - protected $helper; + protected $privacyHelper; /** - * @var Session + * @var CustomerSession */ - protected $session; + protected $customerSession; /** * Index constructor. * * @param Context $context - * @param Data $helper - * @param Session $session + * @param PrivacyHelper $privacyHelper + * @param CustomerSession $customerSession */ - public function __construct(Context $context, Data $helper, Session $session) - { + public function __construct( + Context $context, + PrivacyHelper $privacyHelper, + CustomerSession $customerSession + ) { parent::__construct($context); - $this->helper = $helper; - $this->session = $session; + $this->privacyHelper = $privacyHelper; + $this->customerSession = $customerSession; } /** @@ -62,31 +69,20 @@ public function __construct(Context $context, Data $helper, Session $session) */ public function dispatch(RequestInterface $request) { - if (!$this->session->authenticate()) { - $this->_actionFlag->set('', 'no-dispatch', true); - } - - if (!$this->helper->isModuleEnabled()){ - $this->_forward('no_route'); + if (!$this->privacyHelper->isModuleEnabled()){ + $this->_forward('noroute'); } return parent::dispatch($request); } /** - * Execute controller. + * Execute action. * - * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void + * @return \Magento\Framework\Controller\ResultInterface|ResponseInterface */ public function execute() { - $this->_view->loadLayout(); - - if ($block = $this->_view->getLayout()->getBlock('privacy_settings')) { - $block->setRefererUrl($this->_redirect->getRefererUrl()); - } - - $this->_view->getPage()->getConfig()->getTitle()->set(__('Privacy settings')); - $this->_view->renderLayout(); + return $this->resultFactory->create(ResultFactory::TYPE_PAGE); } } diff --git a/Controller/Settings/UndoDelete.php b/Controller/Settings/UndoDelete.php new file mode 100644 index 0000000..8bcc886 --- /dev/null +++ b/Controller/Settings/UndoDelete.php @@ -0,0 +1,113 @@ +privacyHelper = $privacyHelper; + $this->customerSession = $customerSession; + $this->customerManagement = $customerManagement; + } + + /** + * Dispatch controller. + * + * @param RequestInterface $request + * + * @return \Magento\Framework\App\ResponseInterface + * @throws \Magento\Framework\Exception\NotFoundException + */ + public function dispatch(RequestInterface $request) + { + try { + if ( + !$this->privacyHelper->isModuleEnabled() && + !$this->privacyHelper->isAccountDeletionEnabled() && + !$this->customerManagement->isCustomerToBeDeleted($this->customerSession->getCustomerData()) + ) { + $this->_forward('noroute'); + } + } catch (Exception $e) { + $this->_forward('noroute'); + } + + return parent::dispatch($request); + } + + /** + * Execute action. + * + * @return \Magento\Framework\Controller\ResultInterface|ResponseInterface + */ + public function execute() + { + try { + $this->customerManagement->cancelCustomerDeletion($this->customerSession->getCustomerData()); + + $this->messageManager->addSuccessMessage(__('Your account deletion has been canceled!')); + } catch (Exception $e) { + $this->messageManager->addWarningMessage(__('Something went wrong, please try again later!')); + } + + return $this->resultRedirectFactory->create()->setPath('privacy/settings'); + } +} diff --git a/Cron/Schedule.php b/Cron/Schedule.php old mode 100755 new mode 100644 index 5577ae3..adf21a2 --- a/Cron/Schedule.php +++ b/Cron/Schedule.php @@ -14,34 +14,55 @@ * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Flurrybox\EnhancedPrivacy\Cron; use Exception; -use Flurrybox\EnhancedPrivacy\Api\DataDeleteInterface; -use Flurrybox\EnhancedPrivacy\Helper\Data; -use Flurrybox\EnhancedPrivacy\Model\ResourceModel\CronSchedule\CollectionFactory; +use Flurrybox\EnhancedPrivacy\Api\CustomerManagementInterface; +use Flurrybox\EnhancedPrivacy\Api\Data\ReasonInterfaceFactory; +use Flurrybox\EnhancedPrivacy\Api\Data\ScheduleInterface; +use Flurrybox\EnhancedPrivacy\Api\ReasonRepositoryInterface; +use Flurrybox\EnhancedPrivacy\Api\ScheduleRepositoryInterface; +use Flurrybox\EnhancedPrivacy\Helper\Data as PrivacyHelper; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Framework\Registry; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\Exception\StateException; use Magento\Framework\Stdlib\DateTime\DateTime; use Psr\Log\LoggerInterface; -use Flurrybox\EnhancedPrivacy\Model\ReasonsFactory; -use RuntimeException; /** - * Scheduler to clean accounts marked to be deleted or anonymized. + * Class DeletionSchedule. */ class Schedule { /** - * @var LoggerInterface + * @var PrivacyHelper */ - protected $logger; + protected $privacyHelper; + + /** + * @var DateTime + */ + protected $dateTime; + + /** + * @var ScheduleRepositoryInterface + */ + protected $scheduleRepository; + + /** + * @var SearchCriteriaBuilder + */ + protected $criteriaBuilder; /** - * @var CollectionFactory + * @var CustomerManagementInterface */ - protected $collectionFactory; + protected $customerManagement; /** * @var CustomerRepositoryInterface @@ -49,141 +70,135 @@ class Schedule protected $customerRepository; /** - * @var Registry + * @var ReasonInterfaceFactory */ - protected $registry; + protected $reasonFactory; /** - * @var ReasonsFactory + * @var ReasonRepositoryInterface */ - protected $reasonFactory; + protected $reasonRepository; /** - * @var Data + * @var ResourceConnection */ - protected $helper; + protected $resourceConnection; /** - * @var DateTime + * @var LoggerInterface */ - protected $dateTime; + protected $logger; /** - * @var array|DataDeleteInterface[] + * @var ManagerInterface */ - protected $processors; + protected $eventManager; /** - * AccountCleaner constructor. + * DeletionSchedule constructor. * - * @param LoggerInterface $logger - * @param CollectionFactory $collectionFactory - * @param CustomerRepositoryInterface $customerRepository - * @param Registry $registry - * @param ReasonsFactory $reasonFactory - * @param Data $helper + * @param PrivacyHelper $privacyHelper * @param DateTime $dateTime - * @param DataDeleteInterface[] $processors + * @param ScheduleRepositoryInterface $scheduleRepository + * @param SearchCriteriaBuilder $criteriaBuilder + * @param CustomerManagementInterface $customerManagement + * @param CustomerRepositoryInterface $customerRepository + * @param ReasonInterfaceFactory $reasonFactory + * @param ReasonRepositoryInterface $reasonRepository + * @param ResourceConnection $resourceConnection + * @param LoggerInterface $logger + * @param ManagerInterface $eventManager */ public function __construct( - LoggerInterface $logger, - CollectionFactory $collectionFactory, - CustomerRepositoryInterface $customerRepository, - Registry $registry, - ReasonsFactory $reasonFactory, - Data $helper, + PrivacyHelper $privacyHelper, DateTime $dateTime, - array $processors = [] + ScheduleRepositoryInterface $scheduleRepository, + SearchCriteriaBuilder $criteriaBuilder, + CustomerManagementInterface $customerManagement, + CustomerRepositoryInterface $customerRepository, + ReasonInterfaceFactory $reasonFactory, + ReasonRepositoryInterface $reasonRepository, + ResourceConnection $resourceConnection, + LoggerInterface $logger, + ManagerInterface $eventManager ) { - $this->logger = $logger; - $this->collectionFactory = $collectionFactory; + $this->privacyHelper = $privacyHelper; + $this->dateTime = $dateTime; + $this->scheduleRepository = $scheduleRepository; + $this->criteriaBuilder = $criteriaBuilder; + $this->customerManagement = $customerManagement; $this->customerRepository = $customerRepository; - $this->registry = $registry; $this->reasonFactory = $reasonFactory; - $this->helper = $helper; - $this->dateTime = $dateTime; - $this->processors = $processors; + $this->reasonRepository = $reasonRepository; + $this->resourceConnection = $resourceConnection; + $this->logger = $logger; + $this->eventManager = $eventManager; } /** - * Check for accounts which need to be deleted and delete them. + * Process scheduled customer deletion and anonymization. * * @return void */ public function execute() { - if (!$this->helper->isModuleEnabled() || !$this->helper->isAccountDeletionEnabled()) { + if (!($this->privacyHelper->isModuleEnabled() && $this->privacyHelper->isAccountDeletionEnabled())) { return; } - $cronSchedule = $this->collectionFactory - ->create() - ->addFieldToFilter('scheduled_at', ['lteq' => date('Y-m-d H:i:s', $this->dateTime->gmtTimestamp())]); + $criteria = $this->criteriaBuilder + ->addFilter(ScheduleInterface::SCHEDULED_AT, date('Y-m-d H:i:s', $this->dateTime->gmtTimestamp()), 'lteq') + ->create(); + $collection = $this->scheduleRepository->getList($criteria); - if (!$cronSchedule->getItems()) { - return; - } + $this->eventManager->dispatch('enhancedprivacy_load_schedule_list', ['collection' => $collection]); - try { - $this->registry->register('isSecureArea', true); - } catch (RuntimeException $e) { - // area is already set + if (!$collection->getTotalCount()) { + return; } - foreach ($cronSchedule->getItems() as $item) { + foreach ($collection->getItems() as $schedule) { try { - $this->process( - $this->customerRepository->getById($item->getData('customer_id')), - $item->getData('type') - ); + $this->resourceConnection->getConnection()->beginTransaction(); + + $customer = $this->customerRepository->getById($schedule->getCustomerId()); - $this->saveReason($item->getData('reason')); - $item->getResource()->delete($item); + $this->processCustomer($schedule, $customer); + + $reason = $this->reasonFactory->create(); + $reason->setReason($schedule->getReason()); + + $this->reasonRepository->save($reason); + $this->scheduleRepository->delete($schedule); } catch (Exception $e) { + $this->resourceConnection->getConnection()->rollBack(); $this->logger->error($e->getMessage()); } } } /** - * Process data deletion or anonymization. + * Process customer deletion or anonymization. * + * @param ScheduleInterface $schedule * @param CustomerInterface $customer - * @param string $type * * @return void + * @throws StateException */ - protected function process(CustomerInterface $customer, string $type) + protected function processCustomer(ScheduleInterface $schedule, CustomerInterface $customer) { - foreach ($this->processors as $processor) { - if (!$processor instanceof DataDeleteInterface) { - continue; - } + switch ($schedule->getType()) { + case ScheduleInterface::TYPE_DELETE: + $this->customerManagement->deleteCustomer($customer); + break; - switch ($type) { - case Data::SCHEDULE_TYPE_DELETE: - $processor->delete($customer); - break; + case ScheduleInterface::TYPE_ANONYMIZE: + $this->customerManagement->anonymizeCustomer($customer); + break; - case Data::SCHEDULE_TYPE_ANONYMIZE: - $processor->anonymize($customer); - break; - } + default: + throw new StateException(__('Unknown schedule type!')); } } - - /** - * Save reason why account was deleted or anonymized. - * - * @param string $reason - * @throws Exception - */ - public function saveReason($reason) - { - $model = $this->reasonFactory - ->create() - ->setData('reason', $reason); - - $model->getResource()->save($model); - } -} \ No newline at end of file +} diff --git a/Helper/AccountData.php b/Helper/AccountData.php deleted file mode 100644 index a1579e0..0000000 --- a/Helper/AccountData.php +++ /dev/null @@ -1,100 +0,0 @@ -customerSession = $customerSession; - $this->orderCollectionFactory = $orderCollectionFactory; - $this->scheduleCollectionFactory = $scheduleCollectionFactory; - } - - /** - * Check if customer has orders. - * - * @return bool - */ - public function hasOrders() - { - if (!($customerId = $this->customerSession->getCustomerId())) { - return false; - } - - return (bool) $this->orderCollectionFactory->create($customerId)->getTotalCount(); - } - - /** - * Check if customer is deleting his account. - * - * @return bool - */ - public function isAccountToBeDeleted() - { - if (!($customerId = $this->customerSession->getCustomerId())) { - return false; - } - - if ($this->scheduleCollectionFactory->create()->getItemByColumnValue('customer_id', $customerId)) { - return true; - } - - return false; - } -} diff --git a/Helper/Data.php b/Helper/Data.php index 0f4f8f4..1eabef0 100755 --- a/Helper/Data.php +++ b/Helper/Data.php @@ -14,18 +14,24 @@ * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Flurrybox\EnhancedPrivacy\Helper; +use Flurrybox\EnhancedPrivacy\Api\CustomerManagementInterface; +use Flurrybox\EnhancedPrivacy\Model\Source\Config\Schema; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\App\Helper\AbstractHelper; +use Magento\Framework\App\Helper\Context; use Magento\Store\Model\ScopeInterface; /** - * Module configuration helper. + * Module configuration and utility helper. */ class Data extends AbstractHelper { /** - * Config XML paths + * Configuration paths. */ const CONFIG_ENABLE = 'customer/enhancedprivacy/general/enable'; const CONFIG_INFORMATION_PAGE = 'customer/enhancedprivacy/general/information_page'; @@ -33,6 +39,8 @@ class Data extends AbstractHelper const CONFIG_ACCOUNT_DELETION_ENABLED = 'customer/enhancedprivacy/account/account_deletion_enabled'; const CONFIG_ACCOUNT_DELETION_SCHEMA = 'customer/enhancedprivacy/account/deletion_schema'; const CONFIG_ACCOUNT_DELETION_TIME = 'customer/enhancedprivacy/account/deletion_time'; + const CONFIG_ACCOUNT_DELETION_TITLE = 'customer/enhancedprivacy/account/account_deletion_title'; + const CONFIG_ACCOUNT_DELETION_BUTTON_TEXT = 'customer/enhancedprivacy/account/account_deletion_button_text'; const CONFIG_SUCCESS_MESSAGE = 'customer/enhancedprivacy/account/success_message'; const CONFIG_ACCOUNT_DELETION_INFO = 'customer/enhancedprivacy/account/account_deletion_info'; const CONFIG_ACCOUNT_DELETION_REASON_INFO = 'customer/enhancedprivacy/account/account_delete_reason_info'; @@ -40,22 +48,50 @@ class Data extends AbstractHelper const CONFIG_ACCOUNT_ANONYMIZATION_MESSAGE = 'customer/enhancedprivacy/account/account_anonymization_message'; const CONFIG_ACCOUNT_EXPORT_ENABLED = 'customer/enhancedprivacy/export/account_export_enabled'; const CONFIG_ACCOUNT_EXPORT_INFORMATION = 'customer/enhancedprivacy/export/export_information'; + const CONFIG_ACCOUNT_EXPORT_TITLE = 'customer/enhancedprivacy/export/export_title'; + const CONFIG_ACCOUNT_EXPORT_DATA_BUTTON_TEXT = 'customer/enhancedprivacy/export/export_button_text'; const CONFIG_ACCOUNT_POPUP_NOTIFICATION_ENABLED = 'customer/enhancedprivacy/cookie/popup_notification_enabled'; const CONFIG_ACCOUNT_POPUP_TEXT = 'customer/enhancedprivacy/cookie/popup_text'; /** - * Schedule types + * Schedule types. */ const SCHEDULE_TYPE_DELETE = 'delete'; const SCHEDULE_TYPE_ANONYMIZE = 'anonymize'; /** - * Cookies names + * Cookie name. */ const COOKIE_COOKIES_POLICY = 'cookies-policy'; /** - * Is Flurrybox EnhancedPrivacy module enabled + * Helper constants. + */ + const ANONYMOUS_STR = 'Anonymous'; + const ANONYMOUS_DATE = 1; + + /** + * @var CustomerManagementInterface + */ + protected $customerManagement; + + /** + * Data constructor. + * + * @param Context $context + * @param CustomerManagementInterface $customerManagement + */ + public function __construct( + Context $context, + CustomerManagementInterface $customerManagement + ) { + parent::__construct($context); + + $this->customerManagement = $customerManagement; + } + + /** + * Check if module is enabled. * * @return bool */ @@ -65,7 +101,7 @@ public function isModuleEnabled() } /** - * Get privacy information page url + * Get privacy information page url. * * @return string|null */ @@ -75,7 +111,7 @@ public function getInformationPage() } /** - * Get brief information about privacy + * Get brief information about privacy. * * @return string|null */ @@ -85,7 +121,7 @@ public function getInformation() } /** - * Get success message + * Get success message. * * @return string|null */ @@ -95,7 +131,7 @@ public function getSuccessMessage() } /** - * Is account deletion enabled + * Check if account deletion is enabled. * * @return bool */ @@ -116,7 +152,7 @@ public function getDeletionSchema() } /** - * Get account deletion schema type. + * Get account deletion time. * * @return int */ @@ -126,7 +162,27 @@ public function getDeletionTime() } /** - * Get account deletion information + * Get account deletion button text. + * + * @return string|null + */ + public function getDeletionTitle() + { + return $this->scopeConfig->getValue(self::CONFIG_ACCOUNT_DELETION_TITLE, ScopeInterface::SCOPE_STORE); + } + + /** + * Get account deletion button text. + * + * @return string|null + */ + public function getDeletionButtonText() + { + return $this->scopeConfig->getValue(self::CONFIG_ACCOUNT_DELETION_BUTTON_TEXT, ScopeInterface::SCOPE_STORE); + } + + /** + * Get account deletion information. * * @return string|null */ @@ -136,7 +192,7 @@ public function getAccountDeletionInfo() } /** - * Get information about deletion reason + * Get information about deletion reason. * * @return string|null */ @@ -146,7 +202,7 @@ public function getDeletionReasonInfo() } /** - * Is anonymization message enabled + * Check if anonymization message is enabled. * * @return bool */ @@ -157,7 +213,7 @@ public function isAnonymizationMessageEnabled() } /** - * Returns anonymization message string + * Get anonymization message. * * @return string|null */ @@ -167,7 +223,7 @@ public function getAnonymizationMessage() } /** - * Is account data export enabled + * Check if account data export is enabled. * * @return bool */ @@ -177,7 +233,7 @@ public function isAccountExportEnabled() } /** - * Get information about export + * Get information about export. * * @return string|null */ @@ -187,7 +243,27 @@ public function getAccountExportInformation() } /** - * Is popup notification enabled + * Get account deletion button text. + * + * @return string|null + */ + public function getExportTitle() + { + return $this->scopeConfig->getValue(self::CONFIG_ACCOUNT_EXPORT_TITLE, ScopeInterface::SCOPE_STORE); + } + + /** + * Get account deletion button text. + * + * @return string|null + */ + public function getExportButtonText() + { + return $this->scopeConfig->getValue(self::CONFIG_ACCOUNT_EXPORT_DATA_BUTTON_TEXT, ScopeInterface::SCOPE_STORE); + } + + /** + * Check if popup notification is enabled. * * @return bool */ @@ -198,7 +274,7 @@ public function isPopupNotificationEnabled() } /** - * Get popup notification text + * Get popup notification text. * * @return string|null */ @@ -206,4 +282,29 @@ public function getPopupNotificationText() { return $this->scopeConfig->getValue(self::CONFIG_ACCOUNT_POPUP_TEXT, ScopeInterface::SCOPE_STORE); } -} \ No newline at end of file + + /** + * Get deletion type. + * + * @param CustomerInterface $customer + * + * @return string + */ + public function getDeletionType(CustomerInterface $customer) + { + switch ($this->getDeletionSchema()) { + case Schema::DELETE: + return self::SCHEDULE_TYPE_DELETE; + + case Schema::ANONYMIZE: + return self::SCHEDULE_TYPE_ANONYMIZE; + + case Schema::DELETE_ANONYMIZE: + return $this->customerManagement->hasOrders($customer) ? + self::SCHEDULE_TYPE_DELETE : + self::SCHEDULE_TYPE_ANONYMIZE; + } + + return self::SCHEDULE_TYPE_DELETE; + } +} diff --git a/Helper/RandomGenerator.php b/Helper/RandomGenerator.php deleted file mode 100755 index 8477987..0000000 --- a/Helper/RandomGenerator.php +++ /dev/null @@ -1,68 +0,0 @@ -mathRandom = $mathRandom; - } - - /** - * Retrieve random password. - * - * @param int $length - * - * @return string - * @throws LocalizedException - */ - public function generateStr($length = 10) - { - try { - return $this->mathRandom->getRandomString( - $length, - Random::CHARS_LOWERS . Random::CHARS_UPPERS . Random::CHARS_DIGITS - ); - } catch (LocalizedException $e) { - throw new LocalizedException(__('Something went wrong, please try again later!')); - } - } -} diff --git a/Model/CustomerManagement.php b/Model/CustomerManagement.php new file mode 100644 index 0000000..37dc039 --- /dev/null +++ b/Model/CustomerManagement.php @@ -0,0 +1,152 @@ +orderCollectionFactory = $orderCollectionFactory; + $this->scheduleCollectionFactory = $scheduleCollectionFactory; + $this->processors = $processors; + $this->scheduleResource = $scheduleResource; + } + + /** + * Check if customer has made any order. + * + * @param CustomerInterface $customer + * + * @return bool + */ + public function hasOrders(CustomerInterface $customer) + { + return (bool) $this->orderCollectionFactory->create($customer->getId())->getTotalCount(); + } + + /** + * Check if customer is to be deleted. + * + * @param CustomerInterface $customer + * + * @return bool + */ + public function isCustomerToBeDeleted(CustomerInterface $customer) + { + return (bool) $this->scheduleCollectionFactory + ->create() + ->getItemByColumnValue(ScheduleInterface::CUSTOMER_ID, $customer->getId()); + } + + /** + * Process customer delete. + * + * @param CustomerInterface $customer + * + * @return void + */ + public function deleteCustomer(CustomerInterface $customer) + { + foreach ($this->processors->getDeleteProcessors() as $processor) { + $processor->delete($customer); + } + } + + /** + * Process customer anonymization. + * + * @param CustomerInterface $customer + * + * @return void + */ + public function anonymizeCustomer(CustomerInterface $customer) + { + foreach ($this->processors->getDeleteProcessors() as $processor) { + $processor->anonymize($customer); + } + } + + /** + * Cancel customer deletion or anonymization. + * + * @param CustomerInterface $customer + * + * @return void + * @throws \Exception + */ + public function cancelCustomerDeletion(CustomerInterface $customer) + { + /** @var \Flurrybox\EnhancedPrivacy\Model\CronSchedule $scheduleItem */ + $scheduleItem = $this->scheduleCollectionFactory + ->create() + ->getItemByColumnValue(ScheduleInterface::CUSTOMER_ID, $customer->getId()); + + if (!$scheduleItem) { + return; + } + + $this->scheduleResource->delete($scheduleItem); + } +} diff --git a/Model/Processors.php b/Model/Processors.php new file mode 100644 index 0000000..ecc5db5 --- /dev/null +++ b/Model/Processors.php @@ -0,0 +1,86 @@ +deleteProcessors = $deleteProcessors; + $this->exportProcessors = $exportProcessors; + } + + /** + * Get delete and anonymization processors. + * + * @return DataDeleteInterface[] + */ + public function getDeleteProcessors() + { + foreach ($this->deleteProcessors as $deleteProcessor) { + if (!$deleteProcessor instanceof DataDeleteInterface) { + unset($this->deleteProcessors[$deleteProcessor]); + } + } + + return $this->deleteProcessors; + } + + /** + * Get export processors. + * + * @return DataExportInterface[] + */ + public function getExportProcessors() + { + foreach ($this->exportProcessors as $exportProcessor) { + if (!$exportProcessor instanceof DataExportInterface) { + unset($this->exportProcessors[$exportProcessor]); + } + } + + return $this->exportProcessors; + } +} diff --git a/Model/Reason.php b/Model/Reason.php new file mode 100755 index 0000000..06ff08d --- /dev/null +++ b/Model/Reason.php @@ -0,0 +1,89 @@ +_init(ResourceModel\Reason::class); + } + + /** + * Get reason. + * + * @return string + */ + public function getReason() + { + return $this->getData(self::REASON); + } + + /** + * Get created at time. + * + * @return string + */ + public function getCreatedAt() + { + return $this->getData(self::CREATED_AT); + } + + /** + * Set reason. + * + * @param string $reason + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ReasonInterface + */ + public function setReason(string $reason) + { + $this->setData(self::REASON, $reason); + + return $this; + } + + /** + * Set created at time. + * + * @param string $time + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ReasonInterface + */ + public function setCreatedAt(string $time) + { + $this->setData(self::CREATED_AT, $time); + + return $this; + } +} diff --git a/Model/ReasonRepository.php b/Model/ReasonRepository.php new file mode 100644 index 0000000..e0d4ae0 --- /dev/null +++ b/Model/ReasonRepository.php @@ -0,0 +1,186 @@ +reasonFactory = $reasonFactory; + $this->reasonResource = $reasonResource; + $this->reasonCollectionFactory = $reasonCollectionFactory; + $this->collectionProcessor = $collectionProcessor; + $this->searchResultsFactory = $searchResultsFactory; + } + + /** + * Get reason. + * + * @param int $id + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ReasonInterface + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function get(int $id) + { + $reason = $this->reasonFactory->create(); + $this->reasonResource->load($reason, $id); + + if (!$reason->getReasonId()) { + throw new NoSuchEntityException(__('Requested reason doesn\'t exist!')); + } + + return $reason; + } + + /** + * Get reasons that match specific criteria. + * + * @param SearchCriteriaInterface $searchCriteria + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ReasonSearchResultsInterface + */ + public function getList(SearchCriteriaInterface $searchCriteria) + { + $collection = $this->reasonCollectionFactory->create(); + $collection->addFieldToSelect('*'); + + $this->collectionProcessor->process($searchCriteria, $collection); + + $collection->load(); + + $searchResult = $this->searchResultsFactory->create(); + $searchResult + ->setSearchCriteria($searchCriteria) + ->setItems($collection->getItems()) + ->setTotalCount($collection->getSize()); + + return $searchResult; + } + + /** + * Save reason object. + * + * @param ReasonInterface $reason + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ReasonInterface + * @throws \Magento\Framework\Exception\StateException + */ + public function save(ReasonInterface $reason) + { + try { + $this->reasonResource->save($reason); + } catch (AlreadyExistsException $e) { + throw new StateException(__('Schedule could not be saved!')); + } catch (Exception $e) { + throw new StateException(__('Schedule could not be saved!')); + } + + return $reason; + } + + /** + * Delete reason object. + * + * @param ReasonInterface $reason + * + * @return bool + * @throws \Magento\Framework\Exception\StateException + */ + public function delete(ReasonInterface $reason) + { + try { + $this->reasonResource->delete($reason); + } catch (Exception $e) { + throw new StateException(__('Reason could not be deleted!')); + } + + return true; + } + + /** + * Delete reason by id. + * + * @param int $id + * + * @return bool + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\StateException + */ + public function deleteById(int $id) + { + return $this->delete($this->get($id)); + } +} diff --git a/Model/ResourceModel/Reasons.php b/Model/ResourceModel/Reason.php similarity index 81% rename from Model/ResourceModel/Reasons.php rename to Model/ResourceModel/Reason.php index ba5abf0..d9c3dff 100755 --- a/Model/ResourceModel/Reasons.php +++ b/Model/ResourceModel/Reason.php @@ -14,14 +14,17 @@ * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Flurrybox\EnhancedPrivacy\Model\ResourceModel; +use Flurrybox\EnhancedPrivacy\Api\Data\ReasonInterface; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; /** * Deletion or anonymization reason resource model. */ -class Reasons extends AbstractDb +class Reason extends AbstractDb { /** * Resource initialization. @@ -30,6 +33,6 @@ class Reasons extends AbstractDb */ protected function _construct() { - $this->_init('flurrybox_enhancedprivacy_delete_reasons', 'reason_id'); + $this->_init(ReasonInterface::TABLE, ReasonInterface::ID); } } diff --git a/Model/ResourceModel/Reasons/Collection.php b/Model/ResourceModel/Reason/Collection.php similarity index 76% rename from Model/ResourceModel/Reasons/Collection.php rename to Model/ResourceModel/Reason/Collection.php index 511ab10..5c18e1b 100755 --- a/Model/ResourceModel/Reasons/Collection.php +++ b/Model/ResourceModel/Reason/Collection.php @@ -14,13 +14,15 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\ResourceModel\Reasons; +declare(strict_types=1); -use Flurrybox\EnhancedPrivacy\Model\Reasons; +namespace Flurrybox\EnhancedPrivacy\Model\ResourceModel\Reason; + +use Flurrybox\EnhancedPrivacy\Model\Reason; use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; /** - * Reasons collection. + * Reason collection. */ class Collection extends AbstractCollection { @@ -36,6 +38,6 @@ class Collection extends AbstractCollection */ protected function _construct() { - $this->_init(Reasons::class, \Flurrybox\EnhancedPrivacy\Model\ResourceModel\Reasons::class); + $this->_init(Reason::class, \Flurrybox\EnhancedPrivacy\Model\ResourceModel\Reason::class); } } diff --git a/Model/ResourceModel/CronSchedule.php b/Model/ResourceModel/Schedule.php similarity index 76% rename from Model/ResourceModel/CronSchedule.php rename to Model/ResourceModel/Schedule.php index 1bb92bc..c79d08a 100755 --- a/Model/ResourceModel/CronSchedule.php +++ b/Model/ResourceModel/Schedule.php @@ -14,22 +14,25 @@ * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Flurrybox\EnhancedPrivacy\Model\ResourceModel; +use Flurrybox\EnhancedPrivacy\Api\Data\ScheduleInterface; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; /** * Cron schedule resource model. */ -class CronSchedule extends AbstractDb +class Schedule extends AbstractDb { /** - * Resource initialization + * Resource initialization. * * @return void */ protected function _construct() { - $this->_init('flurrybox_enhancedprivacy_cleanup_schedule', 'schedule_id'); + $this->_init(ScheduleInterface::TABLE, ScheduleInterface::ID); } } diff --git a/Model/ResourceModel/CronSchedule/Collection.php b/Model/ResourceModel/Schedule/Collection.php similarity index 71% rename from Model/ResourceModel/CronSchedule/Collection.php rename to Model/ResourceModel/Schedule/Collection.php index f8989de..3b92dd4 100755 --- a/Model/ResourceModel/CronSchedule/Collection.php +++ b/Model/ResourceModel/Schedule/Collection.php @@ -14,9 +14,11 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\ResourceModel\CronSchedule; +declare(strict_types=1); -use Flurrybox\EnhancedPrivacy\Model\CronSchedule; +namespace Flurrybox\EnhancedPrivacy\Model\ResourceModel\Schedule; + +use Flurrybox\EnhancedPrivacy\Model\Schedule; use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; /** @@ -24,11 +26,6 @@ */ class Collection extends AbstractCollection { - /** - * @var string - */ - protected $_idFieldName = 'schedule_id'; - /** * Resource initialization. * @@ -36,6 +33,6 @@ class Collection extends AbstractCollection */ protected function _construct() { - $this->_init(CronSchedule::class, \Flurrybox\EnhancedPrivacy\Model\ResourceModel\CronSchedule::class); + $this->_init(Schedule::class, \Flurrybox\EnhancedPrivacy\Model\ResourceModel\Schedule::class); } } diff --git a/Model/Schedule.php b/Model/Schedule.php new file mode 100755 index 0000000..628e8e6 --- /dev/null +++ b/Model/Schedule.php @@ -0,0 +1,168 @@ +_init(ResourceModel\Schedule::class); + } + + /** + * Get schedule at time. + * + * @return string + */ + public function getScheduledAt() + { + return $this->getData(self::SCHEDULED_AT); + } + + /** + * Get customer id. + * + * @return int + */ + public function getCustomerId() + { + return $this->getData(self::CUSTOMER_ID); + } + + /** + * Get schedule type. + * + * @return string + */ + public function getType() + { + return $this->getData(self::TYPE); + } + + /** + * Get deletion reason. + * + * @return int + */ + public function getReasonId() + { + return $this->getData(self::REASON_ID); + } + + /** + * Set scheduled at time. + * + * @param string $time + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleInterface + */ + public function setScheduledAt(string $time) + { + $this->setData(self::SCHEDULED_AT, $time); + + return $this; + } + + /** + * Set customer id. + * + * @param int $customerId + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleInterface + */ + public function setCustomerId(int $customerId) + { + $this->setData(self::CUSTOMER_ID, $customerId); + + return $this; + } + + /** + * Set schedule type. + * + * @param string $type + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleInterface + */ + public function setType(string $type) + { + $this->setData(self::TYPE, $type); + + return $this; + } + + /** + * Set deletion reason. + * + * @param int $id + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleInterface + */ + public function setReasonId(int $id) + { + $this->setData(self::REASON_ID, $id); + + return $this; + } + + /** + * Get extension attributes. + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleExtensionInterface + */ + public function getExtensionAttributes() + { + $extensionAttributes = $this->_getExtensionAttributes(); + + if ($extensionAttributes === null) { + /** @var \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleExtensionInterface $extensionAttributes */ + $extensionAttributes = $this->extensionAttributesFactory->create(ScheduleInterface::class); + $this->setExtensionAttributes($extensionAttributes); + } + + return $extensionAttributes; + } + + /** + * Set extension attributes. + * + * @param \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleExtensionInterface $extensionAttributes + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleInterface + */ + public function setExtensionAttributes(ScheduleExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/Model/ScheduleRepository.php b/Model/ScheduleRepository.php new file mode 100644 index 0000000..99c3203 --- /dev/null +++ b/Model/ScheduleRepository.php @@ -0,0 +1,187 @@ +scheduleFactory = $scheduleFactory; + $this->scheduleResource = $scheduleResource; + $this->scheduleCollectionFactory = $scheduleCollectionFactory; + $this->collectionProcessor = $collectionProcessor; + $this->searchResultsFactory = $searchResultsFactory; + } + + /** + * Get schedule. + * + * @param int $id + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleInterface + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function get(int $id) + { + $schedule = $this->scheduleFactory->create(); + $this->scheduleResource->load($schedule, $id); + + if (!$schedule->getId()) { + throw new NoSuchEntityException(__('Requested schedule doesn\'t exist!')); + } + + return $schedule; + } + + /** + * Get schedules that match specific criteria. + * + * @param SearchCriteriaInterface $searchCriteria + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleSearchResultsInterface + */ + public function getList(SearchCriteriaInterface $searchCriteria) + { + $collection = $this->scheduleCollectionFactory->create(); + $collection->addFieldToSelect('*'); + + $this->collectionProcessor->process($searchCriteria, $collection); + + $collection->load(); + + $searchResult = $this->searchResultsFactory->create(); + $searchResult + ->setSearchCriteria($searchCriteria) + ->setItems($collection->getItems()) + ->setTotalCount($collection->getSize()); + + return $searchResult; + } + + /** + * Save schedule object. + * + * @param ScheduleInterface $schedule + * + * @return \Flurrybox\EnhancedPrivacy\Api\Data\ScheduleInterface + * @throws \Magento\Framework\Exception\StateException + */ + public function save(ScheduleInterface $schedule) + { + try { + $this->scheduleResource->save($schedule); + } catch (AlreadyExistsException $e) { + throw new StateException(__('Schedule could not be saved!')); + } catch (Exception $e) { + throw new StateException(__('Schedule could not be saved!')); + } + + return $schedule; + } + + /** + * Delete schedule object. + * + * @param ScheduleInterface $schedule + * + * @return bool + * @throws \Magento\Framework\Exception\StateException + */ + public function delete(ScheduleInterface $schedule) + { + try { + $this->scheduleResource->delete($schedule); + } catch (Exception $e) { + throw new StateException(__('Schedule could not be deleted!')); + } + + return true; + } + + /** + * Delete schedule object by id. + * + * @param int $id + * + * @return bool + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\StateException + */ + public function deleteById(int $id) + { + return $this->delete($this->get($id)); + } +} diff --git a/Model/Source/Config/Schema.php b/Model/Source/Config/Schema.php index 340af33..4412ff3 100644 --- a/Model/Source/Config/Schema.php +++ b/Model/Source/Config/Schema.php @@ -14,6 +14,8 @@ * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Flurrybox\EnhancedPrivacy\Model\Source\Config; use Magento\Framework\Option\ArrayInterface; @@ -23,9 +25,12 @@ */ class Schema implements ArrayInterface { - const DELETE = 0; - const ANONYMIZE = 1; - const DELETE_ANONYMIZE = 2; + /** + * Schemas. + */ + const DELETE = 1; + const ANONYMIZE = 2; + const DELETE_ANONYMIZE = 3; /** * Options getter. @@ -35,18 +40,9 @@ class Schema implements ArrayInterface public function toOptionArray() { return [ - [ - 'value' => self::DELETE, - 'label' => __('Always delete') - ], - [ - 'value' => self::ANONYMIZE, - 'label' => __('Always anonymize') - ], - [ - 'value' => self::DELETE_ANONYMIZE, - 'label' => __('Delete if no orders made, anonymize otherwise') - ] + ['value' => self::DELETE, 'label' => __('Always delete')], + ['value' => self::ANONYMIZE, 'label' => __('Always anonymize')], + ['value' => self::DELETE_ANONYMIZE, 'label' => __('Delete if no orders made, anonymize otherwise')] ]; } diff --git a/Observer/Customer/Logout.php b/Observer/Customer/Logout.php new file mode 100755 index 0000000..63368c4 --- /dev/null +++ b/Observer/Customer/Logout.php @@ -0,0 +1,92 @@ +httpContext = $httpContext; + $this->customerSession = $customerSession; + $this->redirect = $redirect; + } + + /** + * Execute observer. + * + * @param Observer $observer + */ + public function execute(Observer $observer) + { + $loggedIn = $this->httpContext->getValue(CustomerContext::CONTEXT_AUTH); + + if (!$loggedIn) { + return; + } + + $customer = $this->customerSession->getCustomerData(); + + if (!$isAnonymized = $customer->getCustomAttribute(CustomerManagementInterface::ATTRIBUTE_IS_ANONYMIZED)) { + return; + } + + if ($isAnonymized->getValue() === Boolean::VALUE_YES) { + $this->customerSession + ->logout() + ->setBeforeAuthUrl($this->redirect->getRefererUrl()); + } + } +} diff --git a/Observers/CustomerLogout.php b/Observers/CustomerLogout.php deleted file mode 100755 index b447b64..0000000 --- a/Observers/CustomerLogout.php +++ /dev/null @@ -1,114 +0,0 @@ -logger = $logger; - $this->context = $context; - $this->session = $session; - $this->redirect = $redirect; - $this->redirectInterface = $redirectInterface; - $this->scheduleCollectionFactory = $scheduleCollectionFactory; - } - - /** - * Execute observer. - * - * @param Observer $observer - */ - public function execute(Observer $observer) - { - $loggedIn = $this->context->getValue(\Magento\Customer\Model\Context::CONTEXT_AUTH); - - if (!$loggedIn) { - return; - } - - $customer = $this->session->getCustomerData(); - - if (!($isAnonymized = $customer->getCustomAttribute('is_anonymized'))) { - return; - } - - if ($isAnonymized->getValue()) { - $this->session - ->logout() - ->setBeforeAuthUrl($this->redirectInterface->getRefererUrl()); - } - } -} diff --git a/Model/Privacy/Delete/CustomerAddresses.php b/Privacy/Delete/Addresses.php similarity index 62% rename from Model/Privacy/Delete/CustomerAddresses.php rename to Privacy/Delete/Addresses.php index 208321b..859e9a8 100644 --- a/Model/Privacy/Delete/CustomerAddresses.php +++ b/Privacy/Delete/Addresses.php @@ -14,10 +14,12 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Delete; +declare(strict_types=1); + +namespace Flurrybox\EnhancedPrivacy\Privacy\Delete; use Flurrybox\EnhancedPrivacy\Api\DataDeleteInterface; -use Flurrybox\EnhancedPrivacy\Helper\AccountData; +use Flurrybox\EnhancedPrivacy\Helper\Data as PrivacyHelper; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; @@ -25,7 +27,7 @@ /** * Process customer address data. */ -class CustomerAddresses implements DataDeleteInterface +class Addresses implements DataDeleteInterface { /** * @var CustomerRepositoryInterface @@ -58,14 +60,10 @@ public function __construct( * * @return void * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function delete(CustomerInterface $customer) { - $customer = $this->customerRepository->getById($customer->getId()); - $addresses = $customer->getAddresses(); - - if (!$addresses) { + if (!$addresses = $customer->getAddresses()) { return; } @@ -81,32 +79,27 @@ public function delete(CustomerInterface $customer) * * @return void * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function anonymize(CustomerInterface $customer) { - $customer = $this->customerRepository->getById($customer->getId()); - $addresses = $customer->getAddresses(); - - if (!$addresses) { + if (!$addresses = $customer->getAddresses()) { return; } foreach ($addresses as $address) { - $address - ->setCity(AccountData::ANONYMOUS_STR) - ->setCompany(AccountData::ANONYMOUS_STR) - ->setCountryId('US') - ->setFax(AccountData::ANONYMOUS_STR) - ->setPrefix(AccountData::ANONYMOUS_STR) - ->setFirstname(AccountData::ANONYMOUS_STR) - ->setLastname(AccountData::ANONYMOUS_STR) - ->setMiddlename(AccountData::ANONYMOUS_STR) - ->setSuffix(AccountData::ANONYMOUS_STR) - ->setPostcode(AccountData::ANONYMOUS_STR) - ->setRegionId(1) - ->setStreet([AccountData::ANONYMOUS_STR, AccountData::ANONYMOUS_STR]) - ->setTelephone(AccountData::ANONYMOUS_STR); + $address->setCity(PrivacyHelper::ANONYMOUS_STR); + $address->setCompany(PrivacyHelper::ANONYMOUS_STR); + $address->setCountryId('US'); + $address->setFax(PrivacyHelper::ANONYMOUS_STR); + $address->setPrefix(''); + $address->setFirstname(PrivacyHelper::ANONYMOUS_STR); + $address->setLastname(PrivacyHelper::ANONYMOUS_STR); + $address->setMiddlename(''); + $address->setSuffix(''); + $address->setPostcode(PrivacyHelper::ANONYMOUS_STR); + $address->setRegionId(1); + $address->setStreet([PrivacyHelper::ANONYMOUS_STR, PrivacyHelper::ANONYMOUS_STR]); + $address->setTelephone(PrivacyHelper::ANONYMOUS_STR); } $this->customerRepository->save($customer); diff --git a/Model/Privacy/Delete/CustomerCompare.php b/Privacy/Delete/Compare.php similarity index 69% rename from Model/Privacy/Delete/CustomerCompare.php rename to Privacy/Delete/Compare.php index 252417e..8b26b4c 100644 --- a/Model/Privacy/Delete/CustomerCompare.php +++ b/Privacy/Delete/Compare.php @@ -14,21 +14,24 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Delete; +declare(strict_types=1); + +namespace Flurrybox\EnhancedPrivacy\Privacy\Delete; use Flurrybox\EnhancedPrivacy\Api\DataDeleteInterface; use Magento\Catalog\Model\Product\Visibility; -use Magento\Catalog\Model\ResourceModel\Product\Compare\Item\CollectionFactory; +use Magento\Catalog\Model\ResourceModel\Product as ProductCompareResource; +use Magento\Catalog\Model\ResourceModel\Product\Compare\Item\CollectionFactory as ProductCompareCollectionFactory; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Store\Model\StoreManagerInterface; /** * Process customer compare. */ -class CustomerCompare implements DataDeleteInterface +class Compare implements DataDeleteInterface { /** - * @var CollectionFactory + * @var ProductCompareCollectionFactory */ protected $collectionFactory; @@ -37,16 +40,26 @@ class CustomerCompare implements DataDeleteInterface */ protected $storeManager; + /** + * @var ProductCompareResource + */ + protected $productCompareResource; + /** * CustomerCompare constructor. * - * @param CollectionFactory $collectionFactory + * @param ProductCompareCollectionFactory $collectionFactory * @param StoreManagerInterface $storeManager + * @param ProductCompareResource $productCompareResource */ - public function __construct(CollectionFactory $collectionFactory, StoreManagerInterface $storeManager) - { + public function __construct( + ProductCompareCollectionFactory $collectionFactory, + StoreManagerInterface $storeManager, + ProductCompareResource $productCompareResource + ) { $this->collectionFactory = $collectionFactory; $this->storeManager = $storeManager; + $this->productCompareResource = $productCompareResource; } /** @@ -58,7 +71,7 @@ public function __construct(CollectionFactory $collectionFactory, StoreManagerIn */ public function delete(CustomerInterface $customer) { - $this->processCompare($customer->getId()); + $this->processCompare((int) $customer->getId()); } /** @@ -70,7 +83,7 @@ public function delete(CustomerInterface $customer) */ public function anonymize(CustomerInterface $customer) { - $this->processCompare($customer->getId()); + $this->processCompare((int) $customer->getId()); } /** @@ -93,7 +106,7 @@ protected function processCompare(int $customerId) Visibility::VISIBILITY_IN_CATALOG, Visibility::VISIBILITY_BOTH ]) - ->walk('delete'); + ->walk([$this->productCompareResource, 'delete']); } } } diff --git a/Model/Privacy/Delete/CustomerData.php b/Privacy/Delete/Customer.php similarity index 58% rename from Model/Privacy/Delete/CustomerData.php rename to Privacy/Delete/Customer.php index 963c59c..9baef7a 100644 --- a/Model/Privacy/Delete/CustomerData.php +++ b/Privacy/Delete/Customer.php @@ -14,20 +14,24 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Delete; +declare(strict_types=1); +namespace Flurrybox\EnhancedPrivacy\Privacy\Delete; + +use Flurrybox\EnhancedPrivacy\Api\Data\CustomerManagementInterface; use Flurrybox\EnhancedPrivacy\Api\DataDeleteInterface; -use Flurrybox\EnhancedPrivacy\Helper\AccountData; -use Flurrybox\EnhancedPrivacy\Helper\RandomGenerator; +use Flurrybox\EnhancedPrivacy\Helper\Data as PrivacyHelper; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Api\Data\GroupInterface; +use Magento\Eav\Model\Entity\Attribute\Source\Boolean; use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Framework\Math\Random; /** * Process customer data. */ -class CustomerData implements DataDeleteInterface +class Customer implements DataDeleteInterface { /** * @var CustomerRepositoryInterface @@ -35,30 +39,30 @@ class CustomerData implements DataDeleteInterface protected $customerRepository; /** - * @var RandomGenerator + * @var EncryptorInterface */ - protected $randomGenerator; + protected $encryptor; /** - * @var EncryptorInterface + * @var Random */ - protected $encryptor; + protected $random; /** * CustomerData constructor. * * @param CustomerRepositoryInterface $customerRepository - * @param RandomGenerator $randomGenerator * @param EncryptorInterface $encryptor + * @param Random $random */ public function __construct( CustomerRepositoryInterface $customerRepository, - RandomGenerator $randomGenerator, - EncryptorInterface $encryptor + EncryptorInterface $encryptor, + Random $random ) { $this->customerRepository = $customerRepository; - $this->randomGenerator = $randomGenerator; $this->encryptor = $encryptor; + $this->random = $random; } /** @@ -89,22 +93,22 @@ public function anonymize(CustomerInterface $customer) $customer = $this->customerRepository->getById($customer->getId()); $customer - ->setPrefix(AccountData::ANONYMOUS_STR) - ->setFirstname(AccountData::ANONYMOUS_STR) - ->setMiddlename(AccountData::ANONYMOUS_STR) - ->setLastname(AccountData::ANONYMOUS_STR) - ->setSuffix(AccountData::ANONYMOUS_STR) - ->setCreatedAt(AccountData::ANONYMOUS_DATE) - ->setUpdatedAt(AccountData::ANONYMOUS_DATE) - ->setEmail($this->getAnonymousEmail($customer->getId())) - ->setDob(AccountData::ANONYMOUS_DATE) - ->setTaxvat(AccountData::ANONYMOUS_STR) + ->setPrefix(PrivacyHelper::ANONYMOUS_STR) + ->setFirstname(PrivacyHelper::ANONYMOUS_STR) + ->setMiddlename(PrivacyHelper::ANONYMOUS_STR) + ->setLastname(PrivacyHelper::ANONYMOUS_STR) + ->setSuffix(PrivacyHelper::ANONYMOUS_STR) + ->setCreatedAt(PrivacyHelper::ANONYMOUS_DATE) + ->setUpdatedAt(PrivacyHelper::ANONYMOUS_DATE) + ->setEmail($this->getAnonymousEmail((int) $customer->getId())) + ->setDob(PrivacyHelper::ANONYMOUS_DATE) + ->setTaxvat(PrivacyHelper::ANONYMOUS_STR) ->setGender(0) - ->setCustomAttribute('is_anonymized', true) + ->setCustomAttribute(CustomerManagementInterface::ATTRIBUTE_IS_ANONYMIZED, Boolean::VALUE_YES) ->setGroupId(GroupInterface::NOT_LOGGED_IN_ID); $this->customerRepository - ->save($customer, $this->encryptor->getHash($this->randomGenerator->generateStr(64), true)); + ->save($customer, $this->encryptor->getHash($this->generateString(64), true)); } /** @@ -117,6 +121,22 @@ public function anonymize(CustomerInterface $customer) */ protected function getAnonymousEmail(int $customerId) { - return $customerId . $this->randomGenerator->generateStr() . '@' . AccountData::ANONYMOUS_STR . '.com'; + return $customerId . $this->generateString() . '@' . PrivacyHelper::ANONYMOUS_STR . '.com'; + } + + /** + * Retrieve random string. + * + * @param int $length + * + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function generateString(int $length = 10) + { + return $this->random->getRandomString( + $length, + Random::CHARS_LOWERS . Random::CHARS_UPPERS . Random::CHARS_DIGITS + ); } } diff --git a/Model/Privacy/Delete/CustomerQuote.php b/Privacy/Delete/Quote.php similarity index 91% rename from Model/Privacy/Delete/CustomerQuote.php rename to Privacy/Delete/Quote.php index 0f34463..57a6189 100644 --- a/Model/Privacy/Delete/CustomerQuote.php +++ b/Privacy/Delete/Quote.php @@ -14,7 +14,9 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Delete; +declare(strict_types=1); + +namespace Flurrybox\EnhancedPrivacy\Privacy\Delete; use Flurrybox\EnhancedPrivacy\Api\DataDeleteInterface; use Magento\Customer\Api\Data\CustomerInterface; @@ -25,7 +27,7 @@ /** * Process customer quote data. */ -class CustomerQuote implements DataDeleteInterface +class Quote implements DataDeleteInterface { /** * @var CartRepositoryInterface @@ -58,7 +60,7 @@ public function __construct(CartRepositoryInterface $cartRepository, StoreManage */ public function delete(CustomerInterface $customer) { - $this->processQuote($customer->getId()); + $this->processQuote((int) $customer->getId()); } /** @@ -70,7 +72,7 @@ public function delete(CustomerInterface $customer) */ public function anonymize(CustomerInterface $customer) { - $this->processQuote($customer->getId()); + $this->processQuote((int) $customer->getId()); } /** diff --git a/Model/Privacy/Delete/CustomerReviews.php b/Privacy/Delete/Reviews.php similarity index 74% rename from Model/Privacy/Delete/CustomerReviews.php rename to Privacy/Delete/Reviews.php index 23da67c..4df2c92 100644 --- a/Model/Privacy/Delete/CustomerReviews.php +++ b/Privacy/Delete/Reviews.php @@ -14,31 +14,41 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Delete; +declare(strict_types=1); + +namespace Flurrybox\EnhancedPrivacy\Privacy\Delete; use Flurrybox\EnhancedPrivacy\Api\DataDeleteInterface; -use Flurrybox\EnhancedPrivacy\Helper\AccountData; +use Flurrybox\EnhancedPrivacy\Helper\Data as PrivacyHelper; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Review\Model\ResourceModel\Review; use Magento\Review\Model\ResourceModel\Review\CollectionFactory; /** * Process customer reviews. */ -class CustomerReviews implements DataDeleteInterface +class Reviews implements DataDeleteInterface { /** * @var CollectionFactory */ protected $collectionFactory; + /** + * @var Review + */ + protected $reviewResource; + /** * CustomerReviews constructor. * * @param CollectionFactory $collectionFactory + * @param Review $reviewResource */ - public function __construct(CollectionFactory $collectionFactory) + public function __construct(CollectionFactory $collectionFactory, Review $reviewResource) { $this->collectionFactory = $collectionFactory; + $this->reviewResource = $reviewResource; } /** @@ -50,7 +60,7 @@ public function __construct(CollectionFactory $collectionFactory) */ public function delete(CustomerInterface $customer) { - $this->processReviews($customer->getId()); + $this->processReviews((int) $customer->getId()); } /** @@ -62,7 +72,7 @@ public function delete(CustomerInterface $customer) */ public function anonymize(CustomerInterface $customer) { - $this->processReviews($customer->getId()); + $this->processReviews((int) $customer->getId()); } /** @@ -83,9 +93,9 @@ protected function processReviews(int $customerId) foreach ($collection as $review) { /** @var \Magento\Review\Model\Review $review */ - $review->setData('nickname', AccountData::ANONYMOUS_STR); + $review->setData('nickname', PrivacyHelper::ANONYMOUS_STR); } - $collection->walk('save'); + $collection->walk([$this->reviewResource, 'save']); } } diff --git a/Model/Privacy/Delete/CustomerWishlist.php b/Privacy/Delete/Wishlist.php similarity index 70% rename from Model/Privacy/Delete/CustomerWishlist.php rename to Privacy/Delete/Wishlist.php index 7c0eb68..931b84b 100644 --- a/Model/Privacy/Delete/CustomerWishlist.php +++ b/Privacy/Delete/Wishlist.php @@ -14,30 +14,40 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Delete; +declare(strict_types=1); + +namespace Flurrybox\EnhancedPrivacy\Privacy\Delete; use Flurrybox\EnhancedPrivacy\Api\DataDeleteInterface; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Wishlist\Model\ResourceModel\Wishlist as WishlistResource; use Magento\Wishlist\Model\WishlistFactory; /** * Process customer wishlist. */ -class CustomerWishlist implements DataDeleteInterface +class Wishlist implements DataDeleteInterface { /** * @var WishlistFactory */ protected $wishlistFactory; + /** + * @var WishlistResource + */ + protected $wishlistResource; + /** * CustomerWishlist constructor. * * @param WishlistFactory $wishlistFactory + * @param WishlistResource $wishlistResource */ - public function __construct(WishlistFactory $wishlistFactory) + public function __construct(WishlistFactory $wishlistFactory, WishlistResource $wishlistResource) { $this->wishlistFactory = $wishlistFactory; + $this->wishlistResource = $wishlistResource; } /** @@ -50,7 +60,7 @@ public function __construct(WishlistFactory $wishlistFactory) */ public function delete(CustomerInterface $customer) { - $this->processWishlist($customer->getId()); + $this->processWishlist((int) $customer->getId()); } /** @@ -63,7 +73,7 @@ public function delete(CustomerInterface $customer) */ public function anonymize(CustomerInterface $customer) { - $this->processWishlist($customer->getId()); + $this->processWishlist((int) $customer->getId()); } /** @@ -76,7 +86,9 @@ public function anonymize(CustomerInterface $customer) */ protected function processWishlist(int $customerId) { - $wishlist = $this->wishlistFactory->create()->loadByCustomerId($customerId); - $wishlist->getResource()->delete($wishlist); + $wishlist = $this->wishlistFactory + ->create() + ->loadByCustomerId($customerId); + $this->wishlistResource->delete($wishlist); } } diff --git a/Model/Privacy/Export/CustomerAddresses.php b/Privacy/Export/Addresses.php similarity index 77% rename from Model/Privacy/Export/CustomerAddresses.php rename to Privacy/Export/Addresses.php index ee0372f..f772ad6 100644 --- a/Model/Privacy/Export/CustomerAddresses.php +++ b/Privacy/Export/Addresses.php @@ -14,23 +14,19 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Export; +declare(strict_types=1); + +namespace Flurrybox\EnhancedPrivacy\Privacy\Export; use Flurrybox\EnhancedPrivacy\Api\DataExportInterface; -use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Directory\Model\CountryFactory; /** * Export customer addresses. */ -class CustomerAddresses implements DataExportInterface +class Addresses implements DataExportInterface { - /** - * @var CustomerRepositoryInterface - */ - protected $customerRepository; - /** * @var CountryFactory */ @@ -39,12 +35,10 @@ class CustomerAddresses implements DataExportInterface /** * CustomerAddresses constructor. * - * @param CustomerRepositoryInterface $customerRepository * @param CountryFactory $countryFactory */ - public function __construct(CustomerRepositoryInterface $customerRepository, CountryFactory $countryFactory) + public function __construct(CountryFactory $countryFactory) { - $this->customerRepository = $customerRepository; $this->countryFactory = $countryFactory; } @@ -61,12 +55,10 @@ public function __construct(CustomerRepositoryInterface $customerRepository, Cou * @param CustomerInterface $customer * * @return array - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function export(CustomerInterface $customer) { - $addresses = $this->customerRepository->getById($customer->getId())->getAddresses(); + $addresses = $customer->getAddresses(); if (!$addresses) { return null; diff --git a/Model/Privacy/Export/CustomerData.php b/Privacy/Export/Customer.php similarity index 73% rename from Model/Privacy/Export/CustomerData.php rename to Privacy/Export/Customer.php index 81379d3..d59c5a7 100644 --- a/Model/Privacy/Export/CustomerData.php +++ b/Privacy/Export/Customer.php @@ -14,32 +14,18 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Export; +declare(strict_types=1); + +namespace Flurrybox\EnhancedPrivacy\Privacy\Export; use Flurrybox\EnhancedPrivacy\Api\DataExportInterface; -use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; /** * Export customer data. */ -class CustomerData implements DataExportInterface +class Customer implements DataExportInterface { - /** - * @var CustomerRepositoryInterface - */ - protected $customerRepository; - - /** - * CustomerData constructor. - * - * @param CustomerRepositoryInterface $customerRepository - */ - public function __construct(CustomerRepositoryInterface $customerRepository) - { - $this->customerRepository = $customerRepository; - } - /** * Executed upon exporting customer data. * @@ -53,12 +39,9 @@ public function __construct(CustomerRepositoryInterface $customerRepository) * @param CustomerInterface $customer * * @return array - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function export(CustomerInterface $customer) { - $customer = $this->customerRepository->getById($customer->getId()); $genders = [1 => 'Male', 2 => 'Female', 3 => 'Not Specified']; return [ diff --git a/Model/Privacy/Export/CustomerQuote.php b/Privacy/Export/Quote.php similarity index 90% rename from Model/Privacy/Export/CustomerQuote.php rename to Privacy/Export/Quote.php index 3e61491..4e70e09 100644 --- a/Model/Privacy/Export/CustomerQuote.php +++ b/Privacy/Export/Quote.php @@ -13,7 +13,10 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Export; + +declare(strict_types=1); + +namespace Flurrybox\EnhancedPrivacy\Privacy\Export; use Flurrybox\EnhancedPrivacy\Api\DataExportInterface; use Magento\Customer\Api\Data\CustomerInterface; @@ -23,7 +26,7 @@ /** * Export customer quote data. */ -class CustomerQuote implements DataExportInterface +class Quote implements DataExportInterface { /** * @var CartRepositoryInterface @@ -35,11 +38,11 @@ class CustomerQuote implements DataExportInterface * * @param CartRepositoryInterface $cartRepository */ - public function __construct( - CartRepositoryInterface $cartRepository - ) { + public function __construct(CartRepositoryInterface $cartRepository) + { $this->cartRepository = $cartRepository; } + /** * Executed upon exporting customer data. * diff --git a/Model/Privacy/Export/CustomerReviews.php b/Privacy/Export/Reviews.php similarity index 86% rename from Model/Privacy/Export/CustomerReviews.php rename to Privacy/Export/Reviews.php index 8acac21..6cb4a3a 100644 --- a/Model/Privacy/Export/CustomerReviews.php +++ b/Privacy/Export/Reviews.php @@ -14,19 +14,22 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Export; +declare(strict_types=1); + +namespace Flurrybox\EnhancedPrivacy\Privacy\Export; use Flurrybox\EnhancedPrivacy\Api\DataExportInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Review\Model\ResourceModel\Review as ReviewResource; use Magento\Review\Model\ResourceModel\Review\CollectionFactory; use Magento\Review\Model\ReviewFactory; /** * Export customer reviews. */ -class CustomerReviews implements DataExportInterface +class Reviews implements DataExportInterface { /** * @var ReviewFactory @@ -43,21 +46,29 @@ class CustomerReviews implements DataExportInterface */ protected $productRepository; + /** + * @var ReviewResource + */ + protected $reviewResource; + /** * CustomerReviews constructor. * * @param ReviewFactory $reviewFactory * @param CollectionFactory $collectionFactory * @param ProductRepositoryInterface $productRepository + * @param ReviewResource $reviewResource */ public function __construct( ReviewFactory $reviewFactory, CollectionFactory $collectionFactory, - ProductRepositoryInterface $productRepository + ProductRepositoryInterface $productRepository, + ReviewResource $reviewResource ) { $this->reviewFactory = $reviewFactory; $this->collectionFactory = $collectionFactory; $this->productRepository = $productRepository; + $this->reviewResource = $reviewResource; } /** @@ -109,7 +120,7 @@ public function export(CustomerInterface $customer) protected function getReviewProduct(int $reviewId) { $review = $this->reviewFactory->create(); - $review->getResource()->load($review, $reviewId); + $this->reviewResource->load($review, $reviewId); try { $product = $this->productRepository->getById($review->getEntityPkValue()); diff --git a/Model/Privacy/Export/CustomerWishlist.php b/Privacy/Export/Wishlist.php similarity index 95% rename from Model/Privacy/Export/CustomerWishlist.php rename to Privacy/Export/Wishlist.php index 631bd15..e0292e0 100644 --- a/Model/Privacy/Export/CustomerWishlist.php +++ b/Privacy/Export/Wishlist.php @@ -14,7 +14,9 @@ * file that was distributed with this source code. */ -namespace Flurrybox\EnhancedPrivacy\Model\Privacy\Export; +declare(strict_types=1); + +namespace Flurrybox\EnhancedPrivacy\Privacy\Export; use Flurrybox\EnhancedPrivacy\Api\DataExportInterface; use Magento\Catalog\Api\ProductRepositoryInterface; @@ -24,7 +26,7 @@ /** * Export customer wishlist. */ -class CustomerWishlist implements DataExportInterface +class Wishlist implements DataExportInterface { /** * @var WishlistProviderInterface diff --git a/README.md b/README.md old mode 100755 new mode 100644 index b594c2c..76bf284 --- a/README.md +++ b/README.md @@ -1,38 +1,41 @@ -# Magento 2 Enhanced Privacy extension for easier compliance with GDPR # +# Enhanced Privacy extension for Magento 2 -Extension allows customers to delete, anonymize, or export their personal data. +Extension provides easier compliance with GDPR. Allows customers to delete, anonymize, or export their personal data. +View detailed information on [store page](https://flurrybox.com/enhanced-privacy.html). -## Getting Started ## +## Getting Started -### Prerequisites ### +### Prerequisites -Magento 2 Open Source or Commerce edition. +Magento 2 Open Source (CE) or Commerce edition (EE). +Supported versions: Magento 2.1.6+, 2.2.x -### Installation ### +### Installation -#### Composer #### +#### Composer (recommended) -From Magento 2 root folder run the commands: +Commands should be run at the root of your Magento 2 installation. ``` composer require flurrybox/enhanced-privacy php bin/magento module:enable Flurrybox_EnhancedPrivacy php bin/magento setup:upgrade -php bin/magento setup:static-content:deploy php bin/magento setup:di:compile +php bin/magento setup:static-content:deploy ``` -#### Copy files #### +#### Copy package files -1. Copy extension files to the `app/code/Flurrybox/EnhancedPrivacy` directory -2. Run the following commands in Magento 2 root folder: +- Download repository files as ZIP archive +- Extract files to the `app/code/Flurrybox/EnhancedPrivacy` directory +- Run the following commands in Magento 2 root folder: ``` php bin/magento module:enable Flurrybox_EnhancedPrivacy php bin/magento setup:upgrade - php bin/magento setup:static-content:deploy php bin/magento setup:di:compile + php bin/magento setup:static-content:deploy ``` -### Usage and Features ### +### Usage and Features * Configuration for this module is located in 'Stores > Configuration > Customers > Customer Configuration > Privacy (GDPR)'. * Account deletion, anonymization, and export can be done in 'My Account > Privacy Settings'. @@ -41,20 +44,22 @@ php bin/magento setup:di:compile * If customer has made at least one order, they are ineligible to delete their account, instead it will be anonymized. * When a customer visits your store for the first time, a popup notification about cookie policy will be shown. -### Create new export model ### -Besides default export entites its possible to implement custom data export such as - customer data saved in custom database tables by 3rd party integrations. +### Export data +Besides default export entites its possible to implement your own custom data export. When customers will make a request for their personal data export, your class instance will be executed by data export processor and will add new file to data archive. 1. Create a new class implementing `Flurrybox\EnhancedPrivacy\Api\DataExportInterface` interface. ```php ... - + - + ... + ... - Vendor\Module\Model\Privacy\EntityExport + Vendor\Module\Privacy\Export\Entity ... + ... ... ``` -### Create new deletion and anonymization model -To delete data thats gathered by 3rd party integrations you can implement your own data processor. +### Delete and anonymize data +To delete or anonymize data that's gathered by 3rd party integrations you can implement your own data processor. 1. Create a new class implementing `Flurrybox\EnhancedPrivacy\Api\DataDeleteInterface` interface. ```php ... - + - + ... - Vendor\Module\Model\Privacy\EntityDelete + Vendor\Module\Privacy\Delete\Entity ... @@ -153,6 +162,6 @@ To delete data thats gathered by 3rd party integrations you can implement your o ``` -## Copyrights and License ## +## Copyrights and License -Copyright (c) 2018 Flurrybox, Ltd. under GNU General Public License ("GPL") v3.0 +Copyright (c) 2018 Flurrybox, Ltd. under GNU General Public License ("GPL") v3.0 \ No newline at end of file diff --git a/Setup/InstallData.php b/Setup/InstallData.php index 97f7eb1..fdd3369 100755 --- a/Setup/InstallData.php +++ b/Setup/InstallData.php @@ -14,11 +14,15 @@ * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Flurrybox\EnhancedPrivacy\Setup; +use Flurrybox\EnhancedPrivacy\Api\Data\CustomerManagementInterface; use Magento\Customer\Model\Customer; use Magento\Customer\Setup\CustomerSetupFactory; use Magento\Eav\Model\Entity\Attribute\Source\Boolean; +use Magento\Eav\Model\ResourceModel\Entity\Attribute as AttributeResource; use Magento\Framework\Setup\InstallDataInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\ModuleDataSetupInterface; @@ -28,21 +32,26 @@ */ class InstallData implements InstallDataInterface { - const IS_ANONYMIZED_ATTRIBUTE = 'is_anonymized'; - /** * @var CustomerSetupFactory */ protected $customerSetupFactory; + /** + * @var AttributeResource + */ + protected $attributeResource; + /** * InstallData constructor. * * @param CustomerSetupFactory $customerSetupFactory + * @param AttributeResource $attributeResource */ - public function __construct(CustomerSetupFactory $customerSetupFactory) + public function __construct(CustomerSetupFactory $customerSetupFactory, AttributeResource $attributeResource) { $this->customerSetupFactory = $customerSetupFactory; + $this->attributeResource = $attributeResource; } /** @@ -60,37 +69,20 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $installer = $setup; $installer->startSetup(); - $customerSetup = $this->customerSetupFactory->create(['setup' => $setup]); - $customerSetup->removeAttribute(Customer::ENTITY, self::IS_ANONYMIZED_ATTRIBUTE); + $customerSetup = $this->customerSetupFactory->create(); + $customerSetup->removeAttribute(Customer::ENTITY, CustomerManagementInterface::ATTRIBUTE_IS_ANONYMIZED); - $customerSetup->addAttribute(Customer::ENTITY, self::IS_ANONYMIZED_ATTRIBUTE, array( + $customerSetup->addAttribute(Customer::ENTITY, CustomerManagementInterface::ATTRIBUTE_IS_ANONYMIZED, [ 'type' => 'int', - 'label' => 'Is Anonimyzed', + 'label' => 'Is Anonymized', 'input' => 'select', 'source' => Boolean::class, - 'visible' => true, + 'system' => false, + 'user_defined' => false, + 'visible' => false, 'default' => 0, - 'required' => false, - )); - - - $attribute = $customerSetup->getEavConfig()->getAttribute(Customer::ENTITY, self::IS_ANONYMIZED_ATTRIBUTE); - - $attribute - ->setData('used_in_forms', [ - 'adminhtml_customer', - 'checkout_register', - 'customer_account_create', - 'customer_account_edit', - 'adminhtml_checkout' - ]) - ->setData('is_used_for_customer_segment', true) - ->setData('is_system', 0) - ->setData('is_user_defined', 1) - ->setData('is_visible', 1) - ->setData('sort_order', 100); - - $attribute->getResource()->save($attribute); + 'required' => false + ]); $installer->endSetup(); } diff --git a/Setup/InstallSchema.php b/Setup/InstallSchema.php index b5f08b5..e4864aa 100755 --- a/Setup/InstallSchema.php +++ b/Setup/InstallSchema.php @@ -14,8 +14,12 @@ * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Flurrybox\EnhancedPrivacy\Setup; +use Flurrybox\EnhancedPrivacy\Api\Data\ReasonInterface; +use Flurrybox\EnhancedPrivacy\Api\Data\ScheduleInterface; use Magento\Framework\DB\Ddl\Table; use Magento\Framework\Setup\InstallSchemaInterface; use Magento\Framework\Setup\ModuleContextInterface; @@ -35,90 +39,83 @@ class InstallSchema implements InstallSchemaInterface */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { - $installer = $setup; - $installer->startSetup(); + $setup->startSetup(); - /** - * Create table 'flurrybox_enhancedprivacy_delete_reasons' - */ - $table = $installer->getConnection() - ->newTable($installer->getTable('flurrybox_enhancedprivacy_delete_reasons')) + $table = $setup->getConnection() + ->newTable($setup->getTable(ReasonInterface::TABLE)) ->addColumn( - 'reason_id', + ReasonInterface::ID, Table::TYPE_SMALLINT, null, ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true], - 'Status id' + 'Reason Id' )->addColumn( - 'reason', + ReasonInterface::REASON, Table::TYPE_TEXT, null, ['nullable' => false], - 'Reason text' + 'Reason' ) ->addColumn( - 'created_at', + ReasonInterface::CREATED_AT, Table::TYPE_TIMESTAMP, null, ['nullable' => false, 'default' => Table::TIMESTAMP_INIT], 'Created At' ) - ->setComment('Comments statuses'); + ->setComment('Deletion Reasons'); - $installer->getConnection()->createTable($table); + $setup->getConnection()->createTable($table); - /** - * Create table 'flurrybox_enhancedprivacy_cleanup_schedule' - */ - $table = $installer->getConnection() - ->newTable($installer->getTable('flurrybox_enhancedprivacy_cleanup_schedule')) + $table = $setup->getConnection() + ->newTable($setup->getTable(ScheduleInterface::TABLE)) ->addColumn( - 'schedule_id', + ScheduleInterface::ID, Table::TYPE_SMALLINT, null, ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true], - 'Id of schedule item' + 'Schedule Id' ) ->addColumn( - 'scheduled_at', + ScheduleInterface::SCHEDULED_AT, Table::TYPE_TIMESTAMP, null, - ['nullable' => false, 'default' => Table::TIMESTAMP_INIT], + ['nullable' => true], 'Scheduled At' ) ->addColumn( - 'customer_id', + ScheduleInterface::CUSTOMER_ID, Table::TYPE_INTEGER, null, ['nullable' => false], - 'Customer entity Id' + 'Customer Id' ) ->addColumn( - 'type', + ScheduleInterface::TYPE, Table::TYPE_TEXT, 255, ['nullable' => false], - 'Action type' + 'Type' ) ->addColumn( - 'reason', - Table::TYPE_TEXT, + ScheduleInterface::REASON_ID, + Table::TYPE_INTEGER, null, ['nullable' => false], - 'Reason text' + 'Reason Id' ) ->addIndex( $setup->getIdxName( - 'flurrybox_enhancedprivacy_cleanup_schedule', - ['customer_id'], + ScheduleInterface::TABLE, + [ScheduleInterface::CUSTOMER_ID], true ), - ['customer_id'], + [ScheduleInterface::CUSTOMER_ID], ['type' => 'unique'] ) ->setComment('Account Cleanup Schedule'); - $installer->getConnection()->createTable($table); + $setup->getConnection()->createTable($table); $setup->endSetup(); } diff --git a/composer.json b/composer.json index f0f3efe..785723d 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "flurrybox/enhanced-privacy", "description": "Magento 2 module to make your store GDPR compatible", "type": "magento2-module", - "version": "1.0.1", + "version": "2.0.0", "authors": [ { "name": "Flurrybox", @@ -13,7 +13,8 @@ "GPL-3.0-or-later" ], "require": { - "php": "^7.0" + "php": "^7.0", + "flurrybox/module-core": "^1.0.0" }, "autoload": { "files": [ diff --git a/etc/acl.xml b/etc/acl.xml index 4de683b..a7fe050 100755 --- a/etc/acl.xml +++ b/etc/acl.xml @@ -20,8 +20,13 @@ - - + + + + diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml new file mode 100644 index 0000000..1c2183b --- /dev/null +++ b/etc/adminhtml/di.xml @@ -0,0 +1,39 @@ + + + + + + + Flurrybox\EnhancedPrivacy\Model\ExtensionMetaData + + + + + + Enhanced Privacy + Magento 2 module to make your store GDPR compatible + enhanced-privacy + 2.0.0 + https://flurrybox.com/enhanced-privacy.html + https://flurrybox.com/kb/enhanced-privacy.html + + + diff --git a/etc/adminhtml/menu.xml b/etc/adminhtml/menu.xml new file mode 100644 index 0000000..d98d87a --- /dev/null +++ b/etc/adminhtml/menu.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/Model/Reasons.php b/etc/adminhtml/routes.xml old mode 100755 new mode 100644 similarity index 56% rename from Model/Reasons.php rename to etc/adminhtml/routes.xml index e53c675..825900e --- a/Model/Reasons.php +++ b/etc/adminhtml/routes.xml @@ -1,4 +1,5 @@ - + + + + + + + + diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 3bf4eb5..4d44614 100755 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -18,87 +18,80 @@ -
- separator-top - - customer - Magento_Customer::config_customer - + - - Magento\Config\Model\Config\Source\Yesno - - + Magento\Cms\Model\Config\Source\Page 1 - - + 1 - - 1 - - - + Magento\Config\Model\Config\Source\Yesno - - + Flurrybox\EnhancedPrivacy\Model\Source\Config\Schema 1 - - + validate-greater-than-zero required-entry Time in which to process delete and anonymization (minimum is 1 minute) 1 - 1 - 1 - @@ -106,36 +99,33 @@ 1 - - + 1 - - + 1 + sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="1"> Magento\Config\Model\Config\Source\Yesno - + 1 - @@ -144,48 +134,56 @@ - - 1 - - Magento\Config\Model\Config\Source\Yesno - + + + 1 + + + + + + 1 + + + - + 1 - - 1 - - Magento\Config\Model\Config\Source\Yesno - + - - - + + 1 1 diff --git a/etc/config.xml b/etc/config.xml index 99b51f8..4344ee0 100755 --- a/etc/config.xml +++ b/etc/config.xml @@ -37,6 +37,8 @@ You can export the data we have about you using the button below. + Export my data + Export your personal data 1 diff --git a/etc/crontab.xml b/etc/crontab.xml index aeba222..5552279 100755 --- a/etc/crontab.xml +++ b/etc/crontab.xml @@ -18,7 +18,7 @@ - + * * * * * diff --git a/etc/di.xml b/etc/di.xml index 2701834..ef67c47 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -17,39 +17,61 @@ --> - + + + + + + + + + - - Flurrybox\EnhancedPrivacy\Model\Privacy\Delete\CustomerAddresses - Flurrybox\EnhancedPrivacy\Model\Privacy\Delete\CustomerCompare - Flurrybox\EnhancedPrivacy\Model\Privacy\Delete\CustomerWishlist - Flurrybox\EnhancedPrivacy\Model\Privacy\Delete\CustomerReviews - Flurrybox\EnhancedPrivacy\Model\Privacy\Delete\CustomerQuote - Flurrybox\EnhancedPrivacy\Model\Privacy\Delete\CustomerData + + Flurrybox\EnhancedPrivacy\Privacy\Delete\Addresses + Flurrybox\EnhancedPrivacy\Privacy\Delete\Compare + Flurrybox\EnhancedPrivacy\Privacy\Delete\Wishlist + Flurrybox\EnhancedPrivacy\Privacy\Delete\Reviews + Flurrybox\EnhancedPrivacy\Privacy\Delete\Quote + Flurrybox\EnhancedPrivacy\Privacy\Delete\Customer + + + Flurrybox\EnhancedPrivacy\Privacy\Export\Addresses + Flurrybox\EnhancedPrivacy\Privacy\Export\Wishlist + Flurrybox\EnhancedPrivacy\Privacy\Export\Reviews + Flurrybox\EnhancedPrivacy\Privacy\Export\Quote + Flurrybox\EnhancedPrivacy\Privacy\Export\Customer - - + - - Flurrybox\EnhancedPrivacy\Model\Privacy\Export\CustomerAddresses - Flurrybox\EnhancedPrivacy\Model\Privacy\Export\CustomerQuote - Flurrybox\EnhancedPrivacy\Model\Privacy\Export\CustomerData - Flurrybox\EnhancedPrivacy\Model\Privacy\Export\CustomerWishlist - Flurrybox\EnhancedPrivacy\Model\Privacy\Export\CustomerReviews + + Flurrybox\EnhancedPrivacy\Model\ResourceModel\Reason\Grid\Collection + + + flurrybox_enhancedprivacy_delete_reasons + Flurrybox\EnhancedPrivacy\Model\ResourceModel\Reason + + + + + Flurrybox\EnhancedPrivacy\Api\CustomerManagementInterface\Proxy + + diff --git a/etc/events.xml b/etc/events.xml index 80b01d8..7ea5aac 100755 --- a/etc/events.xml +++ b/etc/events.xml @@ -18,6 +18,6 @@ - + diff --git a/etc/frontend/routes.xml b/etc/frontend/routes.xml index 4352d0a..a4e8979 100755 --- a/etc/frontend/routes.xml +++ b/etc/frontend/routes.xml @@ -19,7 +19,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> - + diff --git a/etc/module.xml b/etc/module.xml index 524a674..9e4ee4c 100755 --- a/etc/module.xml +++ b/etc/module.xml @@ -17,5 +17,10 @@ --> - + + + + + + diff --git a/i18n/en_US.csv b/i18n/en_US.csv new file mode 100644 index 0000000..8a6112f --- /dev/null +++ b/i18n/en_US.csv @@ -0,0 +1,69 @@ +"Deletion reasons successfully deleted!","Deletion reasons successfully deleted!" +"Password you typed does not match this account!","Password you typed does not match this account!" +"You did not sign in correctly or your account is temporarily disabled.","You did not sign in correctly or your account is temporarily disabled." +"Something went wrong, please try again later!","Something went wrong, please try again later!" +"Something went wrong, please try again later.","Something went wrong, please try again later." +"Your account deletion has been canceled!","Your account deletion has been canceled!" +"Unknown schedule type!","Unknown schedule type!" +"Requested reason doesn't exist!","Requested reason doesn't exist!" +"Schedule could not be saved!","Schedule could not be saved!" +"Reason could not be deleted!","Reason could not be deleted!" +"Requested schedule doesn't exist!","Requested schedule doesn't exist!" +"Schedule could not be deleted!","Schedule could not be deleted!" +"Always delete","Always delete" +"Always anonymize","Always anonymize" +"Delete if no orders made, anonymize otherwise","Delete if no orders made, anonymize otherwise" +"* Required Fields","* Required Fields" +"Required data","Required data" +Password,Password +Reason,Reason +"Delete my account","Delete my account" +"Go back","Go back" +Information,Information +Attention!,Attention! +"Your account will be deleted soon. You can undo this action.","Your account will be deleted soon. You can undo this action." +"Undo deletion","Undo deletion" +"Cookie Policy","Cookie Policy" +"Click here to learn about cookies","Click here to learn about cookies" +Agree,Agree +"Please enter a number greater than 0 in this field.","Please enter a number greater than 0 in this field." +Attention,Attention +"Do you really want to delete your account?","Do you really want to delete your account?" +"Magento 2 module to make your store GDPR compatible","Magento 2 module to make your store GDPR compatible" +"Deletion Reasons","Deletion Reasons" +"Privacy (GDPR)","Privacy (GDPR)" +General,General +Enable,Enable +"Privacy policy page","Privacy policy page" +"This page should store information about privacy and GDPR policy.","This page should store information about privacy and GDPR policy." +"Brief information","Brief information" +"Brief information about your private policy. This text will be displayed in the settings of the user account.","Brief information about your private policy. This text will be displayed in the settings of the user account." +"Account Deletion And Anonymization","Account Deletion And Anonymization" +"Allow user deletion","Allow user deletion" +"Deletion strategy","Deletion strategy" +"Account deletion time (minutes)","Account deletion time (minutes)" +"Deletion section title","Deletion section title" +"Deletion button text","Deletion button text" +"Success message","Success message" +"This message will be shown when a customer deletes their account.","This message will be shown when a customer deletes their account." +"Deletion information","Deletion information" +"Information about account deletion. This text will be displayed in the settings of the user account.","Information about account deletion. This text will be displayed in the settings of the user account." +"Delete reason description","Delete reason description" +"Description for deletion reason filed. This text will be displayed in the account delete page under reason field.","Description for deletion reason filed. This text will be displayed in the account delete page under reason field." +"Enable anonymization message","Enable anonymization message" +"This message will be show in the settings of the user account, only if the user has made at least one order.","This message will be show in the settings of the user account, only if the user has made at least one order." +"Anonymization message","Anonymization message" +"Customer Data Export","Customer Data Export" +"Allow user export","Allow user export" +"Export information","Export information" +"Information about data export. This text will be displayed in the settings of the user account.","Information about data export. This text will be displayed in the settings of the user account." +"Cookie Policy Popup","Cookie Policy Popup" +"Enable popup notification","Enable popup notification" +"This notification will be displayed at the top of the page until the user agrees to privacy policy.","This notification will be displayed at the top of the page until the user agrees to privacy policy." +"Popup content","Popup content" +"This will be displayed in the popup notification.","This will be displayed in the popup notification." +Delete,Delete +"Delete Reasons","Delete Reasons" +"Are you sure you wan't to delete selected items?","Are you sure you wan't to delete selected items?" +ID,ID +"Deleted/Anonymized At","Deleted/Anonymized At" diff --git a/registration.php b/registration.php index f15f53a..a2fa97f 100755 --- a/registration.php +++ b/registration.php @@ -14,8 +14,6 @@ * file that was distributed with this source code. */ -\Magento\Framework\Component\ComponentRegistrar::register( - \Magento\Framework\Component\ComponentRegistrar::MODULE, - 'Flurrybox_EnhancedPrivacy', - __DIR__ -); +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Flurrybox_EnhancedPrivacy', __DIR__); diff --git a/view/adminhtml/layout/enhancedprivacy_reasons_index.xml b/view/adminhtml/layout/enhancedprivacy_reasons_index.xml new file mode 100644 index 0000000..34080ee --- /dev/null +++ b/view/adminhtml/layout/enhancedprivacy_reasons_index.xml @@ -0,0 +1,28 @@ + + + + + Deletion Reasons + + + + + + + diff --git a/view/adminhtml/ui_component/enhancedprivacy_reasons_listing.xml b/view/adminhtml/ui_component/enhancedprivacy_reasons_listing.xml new file mode 100644 index 0000000..8f2873e --- /dev/null +++ b/view/adminhtml/ui_component/enhancedprivacy_reasons_listing.xml @@ -0,0 +1,116 @@ + + ++ + + enhancedprivacy_reasons_listing.deletion_reasons_listing_data_source + enhancedprivacy_reasons_listing.deletion_reasons_listing_data_source + + spinner_columns + + + + + + Magento_Ui/js/grid/tree-massactions + + + + + + delete + Delete + + + Delete Reasons + Are you sure you wan't to delete selected items? + + + + + + + + + + + Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider + deletion_reasons_listing_data_source + id + id + + + Magento_Ui/js/grid/provider + + + id + + + + + + + + + + false + 50 + id + + + + + + + textRange + asc + ID + + + + + + + text + + text + + true + + + Reason + + + + + + + dateRange + Magento_Ui/js/grid/columns/date + date + Deleted/Anonymized At + + + + + diff --git a/view/frontend/layout/customer_account.xml b/view/frontend/layout/customer_account.xml index 7db9c93..3366b4d 100755 --- a/view/frontend/layout/customer_account.xml +++ b/view/frontend/layout/customer_account.xml @@ -21,10 +21,11 @@ + name="customer-account-navigation-privacy-link"> privacy/settings Privacy settings + 1000 diff --git a/view/frontend/layout/default.xml b/view/frontend/layout/default.xml index 2c78a82..17f4256 100644 --- a/view/frontend/layout/default.xml +++ b/view/frontend/layout/default.xml @@ -19,14 +19,13 @@ xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> - + - + Flurrybox_EnhancedPrivacy/js/view/popup - enhanced-privacy-cookie-policy + enhancedprivacy-cookie-policy diff --git a/view/frontend/layout/privacy_delete_index.xml b/view/frontend/layout/privacy_settings_delete.xml similarity index 79% rename from view/frontend/layout/privacy_delete_index.xml rename to view/frontend/layout/privacy_settings_delete.xml index 89cd56f..7e897af 100755 --- a/view/frontend/layout/privacy_delete_index.xml +++ b/view/frontend/layout/privacy_settings_delete.xml @@ -18,10 +18,13 @@ + + Delete account + - + diff --git a/view/frontend/layout/privacy_settings_index.xml b/view/frontend/layout/privacy_settings_index.xml index d633eeb..12b738f 100755 --- a/view/frontend/layout/privacy_settings_index.xml +++ b/view/frontend/layout/privacy_settings_index.xml @@ -18,10 +18,13 @@ + + Privacy settings + - + diff --git a/view/frontend/templates/account/settings.phtml b/view/frontend/templates/account/settings.phtml deleted file mode 100755 index 377fe38..0000000 --- a/view/frontend/templates/account/settings.phtml +++ /dev/null @@ -1,106 +0,0 @@ -helper(Data::class); -?> -
-
- -
-
-

escapeHtml($helper->getInformation()); ?>

-
-
- -isAccountExportEnabled()) : ?> -
-
- -
-
-

escapeHtml(__($helper->getAccountExportInformation())); ?>

-
-
- -
-
-
-
- - -isAccountDeletionEnabled()) : ?> -
-
- -
-
- isAccountToBeDeleted()): ?> -

escapeHtml($helper->getAccountDeletionInfo()); ?>

- -

-
-
- -
-
- -

escapeHtml($helper->getAccountDeletionInfo()); ?>

- shouldAnonymize()): ?> -

escapeHtml($helper->getAnonymizationMessage()); ?>

- -
-
- -
-
- -
-
- - - diff --git a/view/frontend/templates/account/delete.phtml b/view/frontend/templates/customer/privacy/delete.phtml similarity index 86% rename from view/frontend/templates/account/delete.phtml rename to view/frontend/templates/customer/privacy/delete.phtml index 9f8d317..cb49e22 100755 --- a/view/frontend/templates/account/delete.phtml +++ b/view/frontend/templates/customer/privacy/delete.phtml @@ -13,12 +13,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ - -use Flurrybox\EnhancedPrivacy\Helper\Data; - -/** @var \Flurrybox\EnhancedPrivacy\Block\Account\Delete $block */ -/** @var Data $helper */ -$helper = $this->helper(Data::class); +/** @var \Flurrybox\EnhancedPrivacy\Block\Customer\Privacy\Delete $block */ ?> - - diff --git a/view/frontend/templates/messages/popup.phtml b/view/frontend/templates/customer/privacy_popup.phtml similarity index 87% rename from view/frontend/templates/messages/popup.phtml rename to view/frontend/templates/customer/privacy_popup.phtml index 81a262d..fe6e561 100644 --- a/view/frontend/templates/messages/popup.phtml +++ b/view/frontend/templates/customer/privacy_popup.phtml @@ -13,9 +13,9 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -/** @var \Flurrybox\EnhancedPrivacy\Block\Messages\PrivacyMessagePopup $block */ +/** @var \Flurrybox\EnhancedPrivacy\Block\Customer\PrivacyPopup $block */ ?> -