From 171f81e7b50ed4aed24d2ad27150cac1859b9674 Mon Sep 17 00:00:00 2001 From: gauquier Date: Wed, 7 Oct 2020 11:35:40 +0200 Subject: [PATCH 1/3] Added support for scopable and localizable attributes on remove entity --- .../CheckReferenceDataOnRemovalSubscriber.php | 85 ++++++++++++++++--- Resources/config/event_listeners.yml | 2 + 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/Event/Subscriber/CheckReferenceDataOnRemovalSubscriber.php b/Event/Subscriber/CheckReferenceDataOnRemovalSubscriber.php index 1ae0a39e..8c14ac87 100644 --- a/Event/Subscriber/CheckReferenceDataOnRemovalSubscriber.php +++ b/Event/Subscriber/CheckReferenceDataOnRemovalSubscriber.php @@ -2,10 +2,14 @@ namespace Pim\Bundle\CustomEntityBundle\Event\Subscriber; +use Akeneo\Channel\Component\Repository\ChannelRepositoryInterface; +use Akeneo\Channel\Component\Repository\LocaleRepositoryInterface; use Akeneo\Pim\Enrichment\Component\Product\Query\Filter\Operators; use Akeneo\Pim\Enrichment\Component\Product\Query\ProductQueryBuilderFactoryInterface; +use Akeneo\Pim\Structure\Component\Model\AttributeInterface; use Akeneo\Tool\Component\StorageUtils\Event\RemoveEvent; use Akeneo\Tool\Component\StorageUtils\StorageEvents; +use Akeneo\UserManagement\Bundle\Context\UserContext; use Doctrine\ORM\EntityManagerInterface; use Oro\Bundle\PimDataGridBundle\Datasource\ResultRecord\Orm\ObjectIdHydrator; use Oro\Bundle\PimDataGridBundle\Extension\MassAction\Event\MassActionEvent; @@ -36,6 +40,12 @@ class CheckReferenceDataOnRemovalSubscriber implements EventSubscriberInterface /** @var EntityManagerInterface */ protected $em; + /** @var LocaleRepositoryInterface */ + private $localeRepository; + + /** @var ChannelRepositoryInterface */ + private $channelRepository; + /** * @param AttributeRepository $attributeRepository * @param ProductQueryBuilderFactoryInterface $pqbFactory @@ -46,12 +56,16 @@ public function __construct( AttributeRepository $attributeRepository, ProductQueryBuilderFactoryInterface $pqbFactory, Registry $configRegistry, - EntityManagerInterface $em + EntityManagerInterface $em, + LocaleRepositoryInterface $localeRepository, + ChannelRepositoryInterface $channelRepository ) { $this->attributeRepository = $attributeRepository; $this->pqbFactory = $pqbFactory; $this->configRegistry = $configRegistry; $this->em = $em; + $this->channelRepository = $channelRepository; + $this->localeRepository = $localeRepository; } /** @@ -119,20 +133,65 @@ public function checkReferenceDataUsage(RemoveEvent $event) */ protected function checkProductLink($attributes, array $referenceDataCode) { + foreach ($attributes as $attribute) { - $pqb = $this->pqbFactory->create(); - $pqb->addFilter($attribute->getCode(), Operators::IN_LIST, $referenceDataCode); - $count = $pqb->execute()->count(); - - if (0 !== $count) { - throw new NonRemovableEntityException( - sprintf( - 'Reference data cannot be removed. It is linked to %s product(s) with the attribute "%s"', - $count, - $attribute->getCode() - ) - ); + $channelCodes = $this->channelRepository->getChannelCodes(); + $localeCodes = $this->localeRepository->getActivatedLocaleCodes(); + + if ($attribute->isScopable() && $attribute->isLocalizable()) { + //loop channels and locales + foreach ($channelCodes as $channelCode) { + foreach ($localeCodes as $localeCode) { + $context = [ + 'scope' => $channelCode, + 'locale' => $localeCode, + ]; + + $this->executeQueryWithFilter($referenceDataCode, $attribute, $context); + } + } + + return; + } + + if ($attribute->isScopable() ) { + //loop channels + foreach ($channelCodes as $channelCode) { + $context['scope'] = $channelCode; + $this->executeQueryWithFilter($referenceDataCode, $attribute, $context); + } + + return; + } + + if ($attribute->isLocalizable()) { + //loop locales + foreach ($localeCodes as $localeCode) { + $context['locale'] = $localeCode; + $this->executeQueryWithFilter($referenceDataCode, $attribute, $context); + } + + return; } + + $this->executeQueryWithFilter($referenceDataCode, $attribute); + } + } + + private function executeQueryWithFilter($referenceDataCode, AttributeInterface $attribute, $context = []) + { + $pqb = $this->pqbFactory->create(); + $pqb->addFilter($attribute->getCode(), Operators::IN_LIST, $referenceDataCode, $context); + $count = $pqb->execute()->count(); + + if (0 !== $count) { + throw new NonRemovableEntityException( + sprintf( + 'Reference data cannot be removed. It is linked to %s product(s) with the attribute "%s"', + $count, + $attribute->getCode() + ) + ); } } } diff --git a/Resources/config/event_listeners.yml b/Resources/config/event_listeners.yml index 3f3b50f0..02b4eef1 100644 --- a/Resources/config/event_listeners.yml +++ b/Resources/config/event_listeners.yml @@ -24,5 +24,7 @@ services: - '@pim_catalog.query.product_and_product_model_query_builder_factory' - '@pim_custom_entity.configuration.registry' - '@doctrine.orm.entity_manager' + - '@pim_catalog.repository.locale' + - '@pim_catalog.repository.channel' tags: - { name: kernel.event_subscriber } From 58b06865d23d7fb08001011754d6563b222efb2e Mon Sep 17 00:00:00 2001 From: gauquier Date: Wed, 7 Oct 2020 11:37:16 +0200 Subject: [PATCH 2/3] Added support for scopable and localizable attributes on remove entity --- Event/Subscriber/CheckReferenceDataOnRemovalSubscriber.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Event/Subscriber/CheckReferenceDataOnRemovalSubscriber.php b/Event/Subscriber/CheckReferenceDataOnRemovalSubscriber.php index 8c14ac87..c5631f3b 100644 --- a/Event/Subscriber/CheckReferenceDataOnRemovalSubscriber.php +++ b/Event/Subscriber/CheckReferenceDataOnRemovalSubscriber.php @@ -9,7 +9,6 @@ use Akeneo\Pim\Structure\Component\Model\AttributeInterface; use Akeneo\Tool\Component\StorageUtils\Event\RemoveEvent; use Akeneo\Tool\Component\StorageUtils\StorageEvents; -use Akeneo\UserManagement\Bundle\Context\UserContext; use Doctrine\ORM\EntityManagerInterface; use Oro\Bundle\PimDataGridBundle\Datasource\ResultRecord\Orm\ObjectIdHydrator; use Oro\Bundle\PimDataGridBundle\Extension\MassAction\Event\MassActionEvent; @@ -51,6 +50,8 @@ class CheckReferenceDataOnRemovalSubscriber implements EventSubscriberInterface * @param ProductQueryBuilderFactoryInterface $pqbFactory * @param Registry $configRegistry * @param EntityManagerInterface $em + * @param LocaleRepositoryInterface $localeRepository + * @param ChannelRepositoryInterface $channelRepository */ public function __construct( AttributeRepository $attributeRepository, @@ -133,7 +134,6 @@ public function checkReferenceDataUsage(RemoveEvent $event) */ protected function checkProductLink($attributes, array $referenceDataCode) { - foreach ($attributes as $attribute) { $channelCodes = $this->channelRepository->getChannelCodes(); $localeCodes = $this->localeRepository->getActivatedLocaleCodes(); @@ -154,7 +154,7 @@ protected function checkProductLink($attributes, array $referenceDataCode) return; } - if ($attribute->isScopable() ) { + if ($attribute->isScopable()) { //loop channels foreach ($channelCodes as $channelCode) { $context['scope'] = $channelCode; From b560413311bef4845163b9347a7ed1dcc4b5d70b Mon Sep 17 00:00:00 2001 From: gauquier Date: Fri, 12 Mar 2021 14:29:34 +0100 Subject: [PATCH 3/3] Added better failed error message on delete --- Resources/config/requirejs.yml | 1 + .../js/datagrid/action/delete-action.js | 120 ++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 Resources/public/js/datagrid/action/delete-action.js diff --git a/Resources/config/requirejs.yml b/Resources/config/requirejs.yml index 797ac5c1..dc3ba2e6 100644 --- a/Resources/config/requirejs.yml +++ b/Resources/config/requirejs.yml @@ -12,6 +12,7 @@ config: custom_entity/controller/edit: pimcustomentity/js/controller/custom_entity-edit custom_entity/fetcher: pimcustomentity/js/fetcher/custom_entity-fetcher custom_entity/remover/reference-data: pimcustomentity/js/remover/reference-data-remover + oro/datagrid/delete-action: pimcustomentity/js/datagrid/action/delete-action config: pim/fetcher-registry: diff --git a/Resources/public/js/datagrid/action/delete-action.js b/Resources/public/js/datagrid/action/delete-action.js new file mode 100644 index 00000000..b2be22ec --- /dev/null +++ b/Resources/public/js/datagrid/action/delete-action.js @@ -0,0 +1,120 @@ +/* global define */ +define([ + 'underscore', + 'oro/messenger', + 'oro/translator', + 'pim/dialog', + 'oro/datagrid/model-action', + 'oro/mediator', + 'pim/user-context', + 'oro/datagrid/delete-confirm', + 'pim/fetcher-registry', +], function(_, messenger, __, Dialog, ModelAction, mediator, userContext, DeleteConfirm, FetcherRegistry) { + 'use strict'; + + /** + * Delete action with confirm dialog, triggers REST DELETE request + * + * @export oro/datagrid/delete-action + * @class oro.datagrid.DeleteAction + * @extends oro.datagrid.ModelAction + */ + return ModelAction.extend({ + errorModal: undefined, + + confirmModal: undefined, + + /** + * Initialize view + * + * @param {Object} options + * @param {Backbone.Model} options.model Optional parameter + * @throws {TypeError} If model is undefined + */ + initialize: function(options) { + options = options || {}; + + this.gridName = options.datagrid.name; + + ModelAction.prototype.initialize.apply(this, arguments); + }, + + /** + * Execute delete model + */ + execute: function() { + this.getConfirmDialog(); + }, + + /** + * Confirm delete item + */ + doDelete: function() { + this.model.id = true; + this.model.destroy({ + url: this.getLink(), + wait: true, + error: function(element, response) { + let contentType = response.getResponseHeader('content-type'); + let message = ''; + //Need to check if it is a json because the backend can return an error + if (contentType.indexOf('application/json') !== -1) { + const decodedResponse = JSON.parse(response.responseText); + if (undefined !== decodedResponse.message) { + message = decodedResponse.message; + } else { + message = response.responseJSON; + } + } + + this.showErrorFlashMessage(message); + }.bind(this), + success: function() { + var messageText = __('pim_enrich.entity.' + this.getEntityCode() + '.flash.delete.success'); + messenger.notify('success', messageText); + userContext.initialize(); + + mediator.trigger('grid_action_execute:product-grid:delete'); + mediator.trigger('datagrid:doRefresh:' + this.gridName); + if (this.gridName === 'association-type-grid') { + FetcherRegistry.getFetcher('association-type').clear(); + } + }.bind(this), + }); + }, + + /** + * Get view for confirm modal + */ + getConfirmDialog: function() { + this.confirmModal = DeleteConfirm.getConfirmDialog( + this.getEntityCode(), + this.doDelete.bind(this), + this.getEntityHint(true) + ); + + return this.confirmModal; + }, + + /** + * Get view for error modal + * + * @return {oro.Modal} + */ + showErrorFlashMessage: function(response) { + let message = ''; + + if (typeof response === 'string') { + message = response; + } else { + try { + message = JSON.parse(response).message; + } catch (e) { + message = __('pim_enrich.entity.' + this.getEntityHint() + '.flash.delete.fail'); + } + } + + messenger.notify('error', '' === message ? __('error.removing.' + this.getEntityHint()) : message); + }, + }); +});