diff --git a/vendor/magento/module-shared-catalog/Controller/Adminhtml/SharedCatalog/Configure/Save.php b/vendor/magento/module-shared-catalog/Controller/Adminhtml/SharedCatalog/Configure/Save.php index 15e945774473..f1c5db7e5a5c 100644 --- a/vendor/magento/module-shared-catalog/Controller/Adminhtml/SharedCatalog/Configure/Save.php +++ b/vendor/magento/module-shared-catalog/Controller/Adminhtml/SharedCatalog/Configure/Save.php @@ -27,6 +27,9 @@ /** * Save shared catalog structure and pricing. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ class Save extends Action implements HttpPostActionInterface { @@ -137,7 +140,8 @@ public function execute() try { $resultDiff = $this->diffProcessor->getDiff($currentStorage, $sharedCatalogId); - $storeId = $this->getRequest()->getParam('store_id'); + // store_id filter stand for store group id (group_id from store_group) + $storeId = (int)$this->getRequest()->getParam('store_id'); $sharedCatalog = $this->configureCategory->saveConfiguredCategories( $currentStorage, $sharedCatalogId, @@ -185,7 +189,7 @@ public function execute() * @throws LocalizedException * @throws NoSuchEntityException */ - private function excludeWebsites($storeId, $customerGroupId) + private function excludeWebsites(?int $storeId, int $customerGroupId) { if ($storeId > 0) { $allWebsiteIds = []; @@ -195,8 +199,8 @@ private function excludeWebsites($storeId, $customerGroupId) $allWebsiteIds[] = $website->getId(); } - //get website id which should be included - $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); + //get website id which should be included based on selected store group + $websiteId = $this->storeManager->getGroup($storeId)->getWebsiteId(); //exclude websites from customer group $excludeWebsiteIds = array_diff($allWebsiteIds, [$websiteId]); diff --git a/vendor/magento/module-shared-catalog/Model/Configure/Category.php b/vendor/magento/module-shared-catalog/Model/Configure/Category.php index 4612b47f2da5..b4f4e5277f97 100644 --- a/vendor/magento/module-shared-catalog/Model/Configure/Category.php +++ b/vendor/magento/module-shared-catalog/Model/Configure/Category.php @@ -62,6 +62,7 @@ public function saveConfiguredCategories( $assignedCategoriesIds = $currentStorage->getAssignedCategoriesIds(); $unassignedCategoriesIds = $currentStorage->getUnassignedCategoriesIds(); + //store_id actually stands for store group id ( group_id ) if ($sharedCatalog->getStoreId() === null) { $sharedCatalog->setStoreId($storeId); $this->sharedCatalogRepository->save($sharedCatalog); diff --git a/vendor/magento/module-shared-catalog/Observer/Controller/SaveProduct.php b/vendor/magento/module-shared-catalog/Observer/Controller/SaveProduct.php deleted file mode 100644 index 09781c239090..000000000000 --- a/vendor/magento/module-shared-catalog/Observer/Controller/SaveProduct.php +++ /dev/null @@ -1,135 +0,0 @@ -productSharedCatalogManagement = $productSharedCatalogManagement; - $this->sharedCatalogRepository = $sharedCatalogRepository; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->productSharedCatalogsLoader = $productSharedCatalogsLoader; - } - - /** - * Add product to the selected shared catalogs after saving. - * - * @param Observer $observer - * @return void - */ - public function execute(Observer $observer) - { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $observer->getEvent()->getProduct(); - - $customerGroupIds = $this->retrieveCustomerGroupIds((array)$product->getData('tier_price')); - $sharedCatalogIds = $this->prepareSharedCatalogIds( - (array)$product->getData('shared_catalog'), - (array)$customerGroupIds - ); - $assignedSharedCatalogs = $this->productSharedCatalogsLoader->getAssignedSharedCatalogs($product->getSku()); - $assignedSharedCatalogIds = array_keys($assignedSharedCatalogs); - - $forCreate = array_diff($sharedCatalogIds, $assignedSharedCatalogIds); - foreach ($forCreate as $sharedCatalogId) { - $this->productSharedCatalogManagement->assignProducts($sharedCatalogId, [$product]); - } - - $forDelete = array_diff($assignedSharedCatalogIds, $sharedCatalogIds); - foreach ($forDelete as $sharedCatalogId) { - $this->productSharedCatalogManagement->unassignProducts($sharedCatalogId, [$product]); - } - } - - /** - * Prepare list of shared catalog ids. - * - * @param array $sharedCatalogsIds - * @param array $customerGroupIds - * @return array - */ - private function prepareSharedCatalogIds(array $sharedCatalogsIds, array $customerGroupIds): array - { - if ($customerGroupIds) { - $this->searchCriteriaBuilder->addFilter( - SharedCatalogInterface::CUSTOMER_GROUP_ID, - $customerGroupIds, - 'in' - ); - $searchCriteria = $this->searchCriteriaBuilder->create(); - $sharedCatalogs = $this->sharedCatalogRepository->getList($searchCriteria)->getItems(); - - foreach ($sharedCatalogs as $sharedCatalog) { - if (!in_array($sharedCatalog->getId(), $sharedCatalogsIds)) { - $sharedCatalogsIds[] = $sharedCatalog->getId(); - } - } - } - - return $sharedCatalogsIds; - } - - /** - * Retrieve customer group ids list from tier prices data. - * - * @param array $tierPricesData - * @return array - */ - private function retrieveCustomerGroupIds(array $tierPricesData): array - { - $customerGroups = []; - - foreach ($tierPricesData as $tierPrice) { - if (!isset($tierPrice['delete']) && !empty($tierPrice['cust_group'])) { - $customerGroups[] = $tierPrice['cust_group']; - } - } - - return $customerGroups; - } -} diff --git a/vendor/magento/module-shared-catalog/Plugin/Backend/Block/Adminhtml/Store/SwitcherRolePermissions.php b/vendor/magento/module-shared-catalog/Plugin/Backend/Block/Adminhtml/Store/SwitcherRolePermissions.php new file mode 100644 index 000000000000..9eb25c5d0b29 --- /dev/null +++ b/vendor/magento/module-shared-catalog/Plugin/Backend/Block/Adminhtml/Store/SwitcherRolePermissions.php @@ -0,0 +1,88 @@ +backendAuthSession = $backendAuthSession; + $this->arrayManager = $arrayManager; + } + + /** + * Remove 'All Stores' for website restricted users + * + * @param Switcher $subject + * @param array $result + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetStoreOptionsAsArray( + Switcher $subject, + array $result + ):array { + $role = $this->backendAuthSession->getUser()->getRole(); + $allowedWebsites = $role->getGwsWebsites() ?? []; + if (!$role->getGwsIsAll() && + count(array_diff($this->getAllWebsiteIds(), $allowedWebsites)) !== 0 && + $this->arrayManager->exists(Switcher::ALL_STORES_ID, $result)) { + array_shift($result); + } + return $result; + } + + /** + * Get all website ids excluding admin store. + * + * @return array + */ + private function getAllWebsiteIds(): array + { + if (!count($this->allWebsiteIds)) { + $websites = $this->websiteCollectionFactory->create()->getAllIds(); + $websites = array_filter($websites, function ($websiteId) { + return $websiteId != 0; + }); + $this->allWebsiteIds = $websites; + } + + return $this->allWebsiteIds; + } +} diff --git a/vendor/magento/module-shared-catalog/Plugin/Catalog/Controller/Adminhtml/Product/Save.php b/vendor/magento/module-shared-catalog/Plugin/Catalog/Controller/Adminhtml/Product/Save.php new file mode 100644 index 000000000000..63c398d18d53 --- /dev/null +++ b/vendor/magento/module-shared-catalog/Plugin/Catalog/Controller/Adminhtml/Product/Save.php @@ -0,0 +1,229 @@ +productRepository = $productRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->sharedCatalogRepository = $sharedCatalogRepository; + $this->productSharedCatalogsLoader = $productSharedCatalogsLoader; + $this->productSharedCatalogManagement = $productSharedCatalogManagement; + } + + /** + * Intercepts product save action and performs shared catalog adjustments + * + * @param ProductSave $subject + * @param callable $proceed + * @return mixed + * @throws LocalizedException + * @throws NoSuchEntityException + */ + public function aroundExecute(ProductSave $subject, callable $proceed): mixed + { + try { + $oldProduct = $this->getProductBeforeSave((int)$subject->getRequest()->getParam('id')); + } catch (\Throwable) { + return $proceed(); + } + $returnValue = $proceed(); + $productRequestData = $subject->getRequest()->getParam('product'); + try { + $product = $this->productRepository->get($productRequestData['sku']); + } catch (\Throwable) { + return $returnValue; + } + + $sharedCatalogIds = $this->getSharedCatalogIds($productRequestData, $oldProduct); + + $assignedSharedCatalogs = $this->productSharedCatalogsLoader->getAssignedSharedCatalogs($product->getSku()); + $assignedSharedCatalogIds = array_keys($assignedSharedCatalogs); + + $forCreate = array_diff($sharedCatalogIds, $assignedSharedCatalogIds); + foreach ($forCreate as $sharedCatalogId) { + $this->productSharedCatalogManagement->assignProducts($sharedCatalogId, [$product]); + } + + $forDelete = array_diff($assignedSharedCatalogIds, $sharedCatalogIds); + foreach ($forDelete as $sharedCatalogId) { + $this->productSharedCatalogManagement->unassignProducts($sharedCatalogId, [$product]); + } + + return $returnValue; + } + + /** + * Get product details before save action is processed + * + * @param int $productId + * @return ProductInterface|null + * @throws NoSuchEntityException + */ + private function getProductBeforeSave(int $productId): ?ProductInterface + { + $oldProduct = null; + if ($productId) { + $oldProduct = clone($this->productRepository->getById($productId)); + } + + return $oldProduct; + } + + /** + * Prepare list of shared catalog ids. + * + * @param array $sharedCatalogsIds + * @param array $customerGroupIds + * @return array + * @throws LocalizedException + */ + private function prepareSharedCatalogIds(array $sharedCatalogsIds, array $customerGroupIds): array + { + if ($customerGroupIds) { + $this->searchCriteriaBuilder->addFilter( + SharedCatalogInterface::CUSTOMER_GROUP_ID, + $customerGroupIds, + 'in' + ); + $searchCriteria = $this->searchCriteriaBuilder->create(); + $sharedCatalogs = $this->sharedCatalogRepository->getList($searchCriteria)->getItems(); + $sharedCatalogsIds = []; + foreach ($sharedCatalogs as $sharedCatalog) { + $sharedCatalogsIds[] = $sharedCatalog->getId(); + } + } + + return $sharedCatalogsIds; + } + + /** + * Retrieve customer group ids list from tier prices data. + * + * @param array $tierPricesData + * @return array + */ + private function retrieveCustomerGroupIds(array $tierPricesData): array + { + $customerGroups = []; + + foreach ($tierPricesData as $tierPrice) { + if (!isset($tierPrice['delete']) && !empty($tierPrice['cust_group'])) { + $customerGroups[] = $tierPrice['cust_group']; + } + } + + return $customerGroups; + } + + /** + * Generate Shared Catalog ids taking into account configured tier prices + * + * @param mixed $productRequestData + * @param ProductInterface|null $oldProduct + * @return array + * @throws LocalizedException + */ + public function getSharedCatalogIds(mixed $productRequestData, ?ProductInterface $oldProduct): array + { + $currentCustomerGroupIds = $this->retrieveCustomerGroupIds( + !empty($productRequestData['tier_price']) ? (array)$productRequestData['tier_price'] : [] + ); + if ($oldProduct) { + $oldTierPrices = []; + foreach ($oldProduct->getTierPrices() as $tierPrice) { + $oldTierPrices [] = [ + 'cust_group' => $tierPrice->getCustomerGroupId() + ]; + } + $previousCustomerGroupIds = $this->retrieveCustomerGroupIds($oldTierPrices); + } else { + $previousCustomerGroupIds = []; + } + + $sharedCatalogIds = ( + !empty($productRequestData['shared_catalog']) ? + (array)$productRequestData['shared_catalog'] : + [] + ); + if ($addedCustomerGroupIds = array_diff($currentCustomerGroupIds, $previousCustomerGroupIds)) { + $addedCatalogIds = $this->prepareSharedCatalogIds( + $sharedCatalogIds, + $addedCustomerGroupIds + ); + + $sharedCatalogIds = array_unique(array_merge($sharedCatalogIds, $addedCatalogIds)); + } + + return $sharedCatalogIds; + } +} diff --git a/vendor/magento/module-shared-catalog/Plugin/SharedCatalog/CollectionFilter.php b/vendor/magento/module-shared-catalog/Plugin/SharedCatalog/CollectionFilter.php new file mode 100644 index 000000000000..c9a43d0190c8 --- /dev/null +++ b/vendor/magento/module-shared-catalog/Plugin/SharedCatalog/CollectionFilter.php @@ -0,0 +1,193 @@ +backendAuthSession->getUser() ? + $this->backendAuthSession->getUser()->getRole() : null; + if ($role && !$role->getGwsIsAll() && !$collection->getFlag(self::FILTERED_FLAG_NAME)) { + if (isset($collection->getSelect()->getPart(Select::FROM)['main_table']) + && $this->request->getParam('shared_catalog_id') === null) { + $this->filterSharedCatalogsByAllowedStoreGroupIdsAndAdminUser($collection, $role); + $collection->setFlag(self::FILTERED_FLAG_NAME, true); + } + } + } + + /** + * Filter the shared catalog collection by allowed store ids. + * + * @param Collection $collection + * @param Role $role + * @return void + * @throws \Zend_Db_Select_Exception + */ + private function filterSharedCatalogsByAllowedStoreGroupIdsAndAdminUser(Collection $collection, Role $role): void + { + $allowedStoreGroupIds = $this->getAllowedStoreGroupIds($role); + + //restricted admin user has all websites assigned like full admin, so no need to filter + $allowedWebsites = $role->getGwsWebsites() ?? []; + if (count(array_diff($this->getAllWebsiteIds(), $allowedWebsites)) === 0) { + $this->resetStoreConditions($collection); + return; + } + + $adminUserId = $this->backendAuthSession->getUser()->getId(); + + $this->resetStoreConditions($collection); + + //filter by allowed store ids when store_id is assigned to shared catalog, + //or by created_by for shared catalog without assigned store group + $collection->getSelect() + ->where('main_table.store_id IN (?)', $allowedStoreGroupIds) + ->orWhere('(main_table.store_id IS NULL AND main_table.created_by = ?)', $adminUserId); + } + + /** + * Reset store where conditions + * + * @param Collection $collection + * @return void + * @throws \Zend_Db_Select_Exception + */ + private function resetStoreConditions(Collection $collection): void + { + $where = $collection->getSelect()->getPart(Select::WHERE); + foreach ($where as $key => $value) { + if (str_contains($value, 'main_table.store_id')) { + unset($where[$key]); + } + } + $collection->getSelect()->setPart(Select::WHERE, $where); + } + + /** + * Get allowed store ids for the current role. + * + * @param Role $role + * @return array + */ + private function getAllowedStoreGroupIds(Role $role): array + { + if (!count($this->allowedStoreGroupIds)) { + $this->allowedStoreGroupIds = $role->getGwsStoreGroups() ?? []; + } + + return $this->allowedStoreGroupIds; + } + + /** + * Get all websites ids excluding admin store. + * + * @return array + */ + private function getAllWebsiteIds(): array + { + if (!count($this->allWebsiteIds)) { + $websites = $this->websiteCollectionFactory->create()->getAllIds(); + $websites = array_filter($websites, function ($websiteId) { + return $websiteId != 0; + }); + $this->allWebsiteIds = $websites; + } + + return $this->allWebsiteIds; + } + + /** + * Adds only allowed stores to shared catalog filter count. + * + * @param Collection $collection + * @throws LocalizedException + * @throws \Zend_Db_Select_Exception + */ + public function beforeGetSelectCountSql(Collection $collection): void + { + $this->filterCollection($collection); + } + + /** + * Adds only restricted admin allowed stores to shared catalog filter. + * + * @param Collection $collection + * @param bool $printQuery + * @param bool $logQuery + * @return array + * @throws LocalizedException + * @throws \Zend_Db_Select_Exception + */ + public function beforeLoadWithFilter( + Collection $collection, + bool $printQuery = false, + bool $logQuery = false + ): array { + $this->filterCollection($collection); + + return [$printQuery, $logQuery]; + } +} diff --git a/vendor/magento/module-shared-catalog/Plugin/Ui/DataProvider/WebsiteRolePermissions.php b/vendor/magento/module-shared-catalog/Plugin/Ui/DataProvider/WebsiteRolePermissions.php new file mode 100644 index 000000000000..19334f277cc5 --- /dev/null +++ b/vendor/magento/module-shared-catalog/Plugin/Ui/DataProvider/WebsiteRolePermissions.php @@ -0,0 +1,92 @@ +backendAuthSession = $backendAuthSession; + $this->arrayManager = $arrayManager; + } + + /** + * Remove 'All Stores' for website restricted users + * + * @param Websites $subject + * @param array $result + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetWebsites( + Websites $subject, + array $result + ):array { + $role = $this->backendAuthSession->getUser()->getRole(); + $allowedWebsites = $role->getGwsWebsites() ?? []; + if (!$role->getGwsIsAll() && + count(array_diff($this->getAllWebsiteIds(), $allowedWebsites)) !== 0 && + $this->arrayManager->exists(0, $result)) { + array_shift($result); + } + return $result; + } + + /** + * Get all website ids excluding admin store. + * + * @return array + */ + private function getAllWebsiteIds(): array + { + if (!count($this->allWebsiteIds)) { + $websites = $this->websiteCollectionFactory->create()->getAllIds(); + $websites = array_filter($websites, function ($websiteId) { + return $websiteId != 0; + }); + $this->allWebsiteIds = $websites; + } + + return $this->allWebsiteIds; + } +} diff --git a/vendor/magento/module-shared-catalog/Ui/DataProvider/Configure/AbstractDataProvider.php b/vendor/magento/module-shared-catalog/Ui/DataProvider/Configure/AbstractDataProvider.php index da9714ad5f58..719ce9bbf370 100644 --- a/vendor/magento/module-shared-catalog/Ui/DataProvider/Configure/AbstractDataProvider.php +++ b/vendor/magento/module-shared-catalog/Ui/DataProvider/Configure/AbstractDataProvider.php @@ -5,8 +5,9 @@ */ namespace Magento\SharedCatalog\Ui\DataProvider\Configure; -use Magento\SharedCatalog\Model\Form\Storage\WizardFactory as WizardStorageFactory; use Magento\SharedCatalog\Model\Form\Storage\UrlBuilder; +use Magento\SharedCatalog\Model\Form\Storage\WizardFactory as WizardStorageFactory; +use Magento\Store\Model\Store; /** * Products grid in shared catalog wizard data provider. @@ -68,6 +69,7 @@ public function __construct( */ public function addFilter(\Magento\Framework\Api\Filter $filter) { + //@phpstan-ignore-next-line as adding return statement cause of backward compatibility issue switch ($filter->getField()) { case 'websites': if ($filter->getValue() != 0) { @@ -113,8 +115,17 @@ protected function prepareCollection() $categoryId = !empty($filters['category_id']) ? $filters['category_id'] : ''; $collection = $this->categoryTree->getCategoryProductsCollectionById($categoryId); if (empty($filters['store_id']) && empty($filters['websites'])) { - $collection->setStore(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $collection->setStore(Store::DEFAULT_STORE_ID); } + + //from ui the "Stores" dropdown contains group_id from store_group, + //instead of store_id from store table + //so the default_store_id from the selected store group is chosen for filtering + if (!empty($filters['store_id'])) { + $storeId = $this->storeManager->getGroup($filters['store_id'])->getDefaultStoreId(); + $collection->setStore($storeId); + } + $collection->addWebsiteNamesToResult(); return $collection; diff --git a/vendor/magento/module-shared-catalog/etc/adminhtml/di.xml b/vendor/magento/module-shared-catalog/etc/adminhtml/di.xml index a07a514023c1..30dab48b366f 100644 --- a/vendor/magento/module-shared-catalog/etc/adminhtml/di.xml +++ b/vendor/magento/module-shared-catalog/etc/adminhtml/di.xml @@ -136,4 +136,21 @@ + + + + + + + + + + + + diff --git a/vendor/magento/module-shared-catalog/etc/adminhtml/events.xml b/vendor/magento/module-shared-catalog/etc/adminhtml/events.xml index 117ae7e99615..67af14df7d3a 100644 --- a/vendor/magento/module-shared-catalog/etc/adminhtml/events.xml +++ b/vendor/magento/module-shared-catalog/etc/adminhtml/events.xml @@ -12,7 +12,4 @@ - - - diff --git a/vendor/magento/module-shared-catalog/view/adminhtml/web/js/grid/filters/configure/filters.js b/vendor/magento/module-shared-catalog/view/adminhtml/web/js/grid/filters/configure/filters.js index c2536b28c4fe..ba9694f971c8 100644 --- a/vendor/magento/module-shared-catalog/view/adminhtml/web/js/grid/filters/configure/filters.js +++ b/vendor/magento/module-shared-catalog/view/adminhtml/web/js/grid/filters/configure/filters.js @@ -59,6 +59,7 @@ define([ selectedStoreId = selectedStore.id; } this.storeFilter().value(selectedStoreId); + this.filters['store_id'] = selectedStoreId; this.apply(); },