diff --git a/app/code/Magento/BannerGraphQl/Model/DynamicBlockFormatter.php b/app/code/Magento/BannerGraphQl/Model/DynamicBlockFormatter.php
new file mode 100644
index 00000000000..609bc0f4b89
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/Model/DynamicBlockFormatter.php
@@ -0,0 +1,46 @@
+idEncoder = $idEncoder;
+ }
+
+ /**
+ * Format dynamic block output
+ *
+ * @param Banner $dynamicBlock
+ * @return array
+ */
+ public function format(Banner $dynamicBlock): array
+ {
+ return [
+ 'uid' => $this->idEncoder->encode($dynamicBlock->getBannerId()),
+ 'content' => [
+ 'html' => $dynamicBlock->getBannerContent()
+ ]
+ ];
+ }
+}
diff --git a/app/code/Magento/BannerGraphQl/Model/DynamicBlocks.php b/app/code/Magento/BannerGraphQl/Model/DynamicBlocks.php
new file mode 100644
index 00000000000..59a25c6f545
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/Model/DynamicBlocks.php
@@ -0,0 +1,214 @@
+collectionFactory = $collectionFactory;
+ $this->resourceConnection = $resourceConnection;
+ $this->idEncoder = $idEncoder;
+ $this->bannerSegmentLink = $bannerSegmentLink;
+ $this->customerSegmentsProvider = $customerSegmentsProvider;
+ }
+
+ /**
+ * Get list of dynamic blocks
+ *
+ * @param array $input
+ * @param int|null $customerId
+ * @param int $pageSize
+ * @param int $currentPage
+ * @param int $websiteId
+ * @return Collection
+ * @throws GraphQlInputException
+ * @throws GraphQlNoSuchEntityException
+ */
+ public function getList(array $input, ?int $customerId, int $pageSize, int $currentPage, int $websiteId): Collection
+ {
+ $collection = $this->collectionFactory->create();
+ $collection->getSelect()
+ ->columns(['main_table.banner_id', 'mbc.banner_content'])
+ ->join(
+ ['mbc' => $this->resourceConnection->getTableName('magento_banner_content')],
+ 'main_table.banner_id = mbc.banner_id',
+ []
+ );
+
+ $collection = $this->addTypeFilter($collection, $input['type'], $input['dynamic_block_uids'] ?? []);
+ $collection = $this->addCustomerSegmentFilter($collection, $customerId, $websiteId);
+
+ if (isset($input['locations'])) {
+ $collection = $this->addLocationFilter($collection, $input['locations']);
+ }
+
+ $collection->addFieldToFilter('is_enabled', Banner::STATUS_ENABLED);
+
+ $collection->getSelect()->group('main_table.banner_id');
+ $collection->setPageSize($pageSize);
+ $collection->setCurPage($currentPage);
+
+ return $collection;
+ }
+
+ /**
+ * Filter dynamic blocks by type
+ *
+ * @param Collection $collection
+ * @param string $type
+ * @param array $uids
+ * @return Collection
+ * @throws GraphQlInputException
+ */
+ private function addTypeFilter(Collection $collection, string $type, array $uids): Collection
+ {
+ switch ($type) {
+ case self::SPECIFIED:
+ if (!empty($uids)) {
+ $ids = $this->prepareDynamicBlockIds($uids);
+ $collection->addFieldToFilter('banner_id', ['in' => $ids]);
+ }
+ break;
+ case self::CART_PRICE_RULE_RELATED:
+ $collection->getSelect()->join(
+ ['mbsr' => $this->resourceConnection->getTableName('magento_banner_salesrule')],
+ 'main_table.banner_id = mbsr.banner_id',
+ []
+ );
+ break;
+ case self::CATALOG_PRICE_RULE_RELATED:
+ $collection->getSelect()->join(
+ ['mbcr' => $this->resourceConnection->getTableName('magento_banner_catalogrule')],
+ 'main_table.banner_id = mbcr.banner_id',
+ []
+ );
+ break;
+ default:
+ throw new GraphQlInputException(__('Incorrect value of input.type'));
+ }
+ return $collection;
+ }
+
+ /**
+ * Filter dynamic blocks by locations
+ *
+ * @param Collection $collection
+ * @param array $locations
+ * @return Collection
+ */
+ private function addLocationFilter(Collection $collection, array $locations): Collection
+ {
+ $filter = [];
+
+ foreach ($locations as $location) {
+ $filter[] = ['like' => new \Zend_Db_Expr("'%{$location}%'")];
+ }
+
+ return $collection->addFieldToFilter('types', $filter);
+ }
+
+ /**
+ * Prepare dynamic block ids
+ *
+ * @param array $dynamicBlockUids
+ * @return array
+ * @throws GraphQlInputException
+ */
+ private function prepareDynamicBlockIds(array $dynamicBlockUids): array
+ {
+ $ids = [];
+ foreach ($dynamicBlockUids as $dynamicBlockUid) {
+ $ids[] = $this->idEncoder->decode($dynamicBlockUid);
+ }
+
+ return $ids;
+ }
+
+ /**
+ * Filter dynamic blocks by customer segment
+ *
+ * @param Collection $collection
+ * @param int|null $customerId
+ * @param int $websiteId
+ * @return Collection
+ * @throws GraphQlNoSuchEntityException
+ */
+ private function addCustomerSegmentFilter(Collection $collection, ?int $customerId, int $websiteId): Collection
+ {
+ try {
+ $customerSegmentIds = $this->customerSegmentsProvider->getCustomerSegmentIdsByCustomerId(
+ $customerId,
+ $websiteId
+ );
+ } catch (NoSuchEntityException $e) {
+ throw new GraphQlNoSuchEntityException(__($e->getMessage()));
+ }
+
+ $this->bannerSegmentLink->addBannerSegmentFilter($collection->getSelect(), $customerSegmentIds);
+
+ return $collection;
+ }
+}
diff --git a/app/code/Magento/BannerGraphQl/Model/Resolver/DynamicBlocks.php b/app/code/Magento/BannerGraphQl/Model/Resolver/DynamicBlocks.php
new file mode 100644
index 00000000000..8209f5ba9f5
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/Model/Resolver/DynamicBlocks.php
@@ -0,0 +1,86 @@
+dynamicBlocks = $dynamicBlocks;
+ $this->formatter = $formatter;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
+ {
+ $customerId = $context->getUserId();
+ $websiteId = (int)$context->getExtensionAttributes()->getStore()->getWebsiteId();
+
+ if ($args['pageSize'] < 1) {
+ throw new GraphQlInputException(__('pageSize value must be greater than 0.'));
+ }
+
+ if ($args['currentPage'] < 1) {
+ throw new GraphQlInputException(__('currentPage value must be greater than 0.'));
+ }
+
+ $dynamicBlockCollection = $this->dynamicBlocks->getList(
+ $args['input'],
+ $customerId,
+ $args['pageSize'],
+ $args['currentPage'],
+ $websiteId
+ );
+
+ $dynamicBlocks = [];
+ foreach ($dynamicBlockCollection->getItems() as $dynamicBlock) {
+ $dynamicBlocks[] = $this->formatter->format($dynamicBlock);
+ }
+
+ $pageSize = $dynamicBlockCollection->getPageSize();
+ $totalCount = $dynamicBlockCollection->getSize();
+
+ return [
+ 'items' => $dynamicBlocks,
+ 'page_info' => [
+ 'page_size' => $pageSize,
+ 'current_page' => $dynamicBlockCollection->getCurPage(),
+ 'total_pages' => $pageSize ? ((int)ceil($totalCount / $pageSize)) : 0,
+ ],
+ 'total_count' => $totalCount
+ ];
+ }
+}
diff --git a/app/code/Magento/BannerGraphQl/Plugin/Block/Widget/Banner.php b/app/code/Magento/BannerGraphQl/Plugin/Block/Widget/Banner.php
new file mode 100644
index 00000000000..dee42e46cb8
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/Plugin/Block/Widget/Banner.php
@@ -0,0 +1,66 @@
+idEncoder = $idEncoder;
+ }
+
+ /**
+ * After plugin for widget with banner
+ *
+ * @param BannerWidget $subject
+ * @param $result
+ * @return string
+ */
+ public function afterGetWidgetAttributes(BannerWidget $subject, $result): string
+ {
+ if (empty($subject->getBannerIds())) {
+ $bannerUids = '';
+ } else {
+ $bannerUids = $this->getBannerUids($subject);
+ }
+
+ $result .= " data-uids=\"{$bannerUids}\"";
+ return $result;
+ }
+
+ /**
+ * @param BannerWidget$subject
+ * @return string
+ */
+ private function getBannerUids(BannerWidget $subject): string
+ {
+ $uids = [];
+
+ $bannerIds = explode(',', $subject->getBannerIds());
+ foreach ($bannerIds as $bannerId) {
+ $uids[] = $this->idEncoder->encode($bannerId);
+ }
+
+ return implode(',', $uids);
+ }
+}
diff --git a/app/code/Magento/BannerGraphQl/README.md b/app/code/Magento/BannerGraphQl/README.md
new file mode 100644
index 00000000000..516bb475fac
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/README.md
@@ -0,0 +1,2 @@
+The Banner module allows creating and managing dynamic blocks and widgets in Magento application.
+The Dynamic Block content can be specified by Store View.
\ No newline at end of file
diff --git a/app/code/Magento/BannerGraphQl/composer.json b/app/code/Magento/BannerGraphQl/composer.json
new file mode 100644
index 00000000000..8f6ebc8b639
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "magento/module-banner-graph-ql",
+ "description": "N/A",
+ "config": {
+ "sort-packages": true
+ },
+ "require": {
+ "magento/framework": "*",
+ "magento/module-banner": "*",
+ "magento/module-banner-customer-segment": "*",
+ "magento/module-customer-segment": "*",
+ "php": "~7.3.0||~7.4.0"
+ },
+ "suggest": {
+ "magento/module-website-restriction": "*"
+ },
+ "type": "magento2-module",
+ "license": [
+ "proprietary"
+ ],
+ "autoload": {
+ "files": [
+ "registration.php"
+ ],
+ "psr-4": {
+ "Magento\\BannerGraphQL\\": ""
+ }
+ }
+}
diff --git a/app/code/Magento/BannerGraphQl/etc/graphql/di.xml b/app/code/Magento/BannerGraphQl/etc/graphql/di.xml
new file mode 100644
index 00000000000..d0869a5297c
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/etc/graphql/di.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/BannerGraphQl/etc/module.xml b/app/code/Magento/BannerGraphQl/etc/module.xml
new file mode 100644
index 00000000000..c781078f04d
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/etc/module.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/BannerGraphQl/etc/schema.graphqls b/app/code/Magento/BannerGraphQl/etc/schema.graphqls
new file mode 100644
index 00000000000..3c96bc01ae9
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/etc/schema.graphqls
@@ -0,0 +1,41 @@
+# Copyright © Magento, Inc. All rights reserved.
+# See COPYING.txt for license details.
+
+type Query {
+ dynamicBlocks(
+ input: DynamicBlocksFilterInput
+ pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. The default is 20")
+ currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1"),
+ ): DynamicBlocks! @resolver(class: "Magento\\BannerGraphQl\\Model\\Resolver\\DynamicBlocks") @doc(description: "Return a list of dynamic blocks filtered by type, location, or UIDs")
+}
+
+type DynamicBlock {
+ uid: ID! @doc(description: "The unique ID of a `DynamicBlock` object")
+ content: ComplexTextValue! @doc(description: "Contains the renderable HTML code of the dynamic block")
+}
+
+type DynamicBlocks {
+ items: [DynamicBlock]! @doc(description: "An array containing individual dynamic blocks")
+ page_info: SearchResultPageInfo @doc(description: "Metadata for pagination rendering")
+ total_count: Int! @doc(description: "The number of returned dynamic blocks")
+}
+
+input DynamicBlocksFilterInput {
+ type: DynamicBlockTypeEnum! @doc(description: "A value indicating the type of dynamic block to filter on")
+ locations: [DynamicBlockLocationEnum!] @doc(description: "An array indicating the locations the dynamic block can be placed")
+ dynamic_block_uids: [ID!] @doc(description: "An array of dynamic block UIDs to filter on")
+}
+
+enum DynamicBlockTypeEnum {
+ SPECIFIED
+ CART_PRICE_RULE_RELATED
+ CATALOG_PRICE_RULE_RELATED
+}
+
+enum DynamicBlockLocationEnum {
+ CONTENT
+ HEADER
+ FOOTER
+ LEFT
+ RIGHT
+}
diff --git a/app/code/Magento/BannerGraphQl/etc/widget.xml b/app/code/Magento/BannerGraphQl/etc/widget.xml
new file mode 100644
index 00000000000..e6e211b0b0f
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/etc/widget.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Displays Specified or Promotion Dynamic Blocks
+
+
+
+
+
+
diff --git a/app/code/Magento/BannerGraphQl/registration.php b/app/code/Magento/BannerGraphQl/registration.php
new file mode 100644
index 00000000000..016d1ede8c7
--- /dev/null
+++ b/app/code/Magento/BannerGraphQl/registration.php
@@ -0,0 +1,9 @@
+getExtensionAttributes()->getStore()->getId();
- $customerGroupId = $this->getDefaultCustomerGroupId($storeId);
+ $storeId = (int)$context->getExtensionAttributes()->getStore()->getId();
+ $customerGroupId = (int)$this->getDefaultCustomerGroupId($storeId);
}
return $customerGroupId;
diff --git a/vendor/magento/module-catalog-permissions-graph-ql/Plugin/CatalogGraphQl/ProductSearchCriteriaFilter.php b/vendor/magento/module-catalog-permissions-graph-ql/Plugin/CatalogGraphQl/ProductSearchCriteriaFilter.php
new file mode 100644
index 00000000000..d82cdbb03b5
--- /dev/null
+++ b/vendor/magento/module-catalog-permissions-graph-ql/Plugin/CatalogGraphQl/ProductSearchCriteriaFilter.php
@@ -0,0 +1,122 @@
+catalogPermissionsConfig = $catalogPermissionsConfig;
+ $this->filterBuilder = $filterBuilder;
+ $this->filterGroupBuilder = $filterGroupBuilder;
+ $this->productAttributeProvider = $productAttributeProvider;
+ $this->fieldNameResolver = $fieldNameResolver;
+ $this->context = $contextFactory->create();
+ }
+
+ /**
+ * Add catalog permission filter to search criteria.
+ *
+ * @param SearchCriteriaBuilder $searchCriteriaBuilder
+ * @param SearchCriteriaInterface $searchCriteria
+ * @param array $args
+ * @param bool $includeAggregation
+ * @return SearchCriteriaInterface
+ * @SuppressWarnings(PHPMD.UnusedLocalVariable)
+ */
+ public function afterBuild(
+ SearchCriteriaBuilder $searchCriteriaBuilder,
+ SearchCriteriaInterface $searchCriteria,
+ array $args,
+ bool $includeAggregation
+ ): SearchCriteriaInterface {
+ if ($this->catalogPermissionsConfig->isEnabled()) {
+ $storeId = (int) $this->context->getExtensionAttributes()->getStore()->getId();
+ $customerGroupId = (int) $this->context->getExtensionAttributes()->getCustomerGroupId();
+
+ $categoryPermissionAttribute = $this->productAttributeProvider->getByAttributeCode('category_permission');
+ $categoryPermissionField = $this->fieldNameResolver->getFieldName(
+ $categoryPermissionAttribute,
+ ['storeId' => $storeId, 'customerGroupId' => $customerGroupId]
+ );
+ $filters = [
+ 'category_permissions_field' => $categoryPermissionField,
+ 'category_permissions_value' => Permission::PERMISSION_DENY,
+ ];
+ foreach ($filters as $field => $value) {
+ $filter = $this->filterBuilder->setField($field)
+ ->setValue($value)
+ ->create();
+ $this->filterGroupBuilder->addFilter($filter);
+ }
+ $filterGroups = $searchCriteria->getFilterGroups();
+ $filterGroups[] = $this->filterGroupBuilder->create();
+ $searchCriteria->setFilterGroups($filterGroups);
+ }
+
+ return $searchCriteria;
+ }
+}
diff --git a/vendor/magento/module-catalog-permissions-graph-ql/etc/di.xml b/vendor/magento/module-catalog-permissions-graph-ql/etc/di.xml
index 8338f9b9d3f..6c50157bea7 100644
--- a/vendor/magento/module-catalog-permissions-graph-ql/etc/di.xml
+++ b/vendor/magento/module-catalog-permissions-graph-ql/etc/di.xml
@@ -30,4 +30,7 @@
+
+
+
diff --git a/vendor/magento/module-catalog-permissions-graph-ql/etc/search_request.xml b/vendor/magento/module-catalog-permissions-graph-ql/etc/search_request.xml
new file mode 100644
index 00000000000..34e7fffa8ea
--- /dev/null
+++ b/vendor/magento/module-catalog-permissions-graph-ql/etc/search_request.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vendor/magento/module-customer-segment/Model/CustomerSegmentsProvider.php b/vendor/magento/module-customer-segment/Model/CustomerSegmentsProvider.php
new file mode 100644
index 00000000000..565733506f1
--- /dev/null
+++ b/vendor/magento/module-customer-segment/Model/CustomerSegmentsProvider.php
@@ -0,0 +1,70 @@
+collectionFactory = $collectionFactory;
+ }
+
+ /**
+ * Get customer segment ids by customer id, if customer id is null return segments for visitor
+ *
+ * @param int|null $customerId
+ * @param int $websiteId
+ * @return array
+ */
+ public function getCustomerSegmentIdsByCustomerId(?int $customerId, int $websiteId): array
+ {
+ $collection = $this->collectionFactory->create();
+ $collection->addIsActiveFilter(1);
+ $collection->addWebsiteFilter($websiteId);
+
+ $customerSegmentIds = [];
+ if ($customerId) {
+ $collection->addFieldToFilter(
+ 'apply_to',
+ [Segment::APPLY_TO_REGISTERED, Segment::APPLY_TO_VISITORS_AND_REGISTERED]
+ );
+ foreach ($collection as $segment) {
+ if ($segment->validateCustomer($customerId, $websiteId)) {
+ $customerSegmentIds[] = $segment->getId();
+ }
+ }
+ } else {
+ $collection->addFieldToFilter(
+ 'apply_to',
+ [Segment::APPLY_TO_VISITORS, Segment::APPLY_TO_VISITORS_AND_REGISTERED]
+ );
+ foreach ($collection as $segment) {
+ $conditions = $segment->getConditions()->asArray();
+ if (empty($conditions['conditions'])) {
+ $customerSegmentIds[] = $segment->getId();
+ }
+ }
+ }
+
+ return $customerSegmentIds;
+ }
+}