diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml deleted file mode 100644 index e02c34fd8868e..0000000000000 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - <description value="Checks to see if the tracking script is in the dom of admin and if setting is turned to no it checks if the tracking script in the dom was removed"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-18192"/> - <group value="backend"/> - <group value="login"/> - </annotations> - - <!-- Logging in Magento admin --> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - </test> -</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Model/Plugin/CustomerAuthorization.php b/app/code/Magento/Customer/Model/Plugin/CustomerAuthorization.php index 9eb9ffb806c9f..b877b2cca67a5 100644 --- a/app/code/Magento/Customer/Model/Plugin/CustomerAuthorization.php +++ b/app/code/Magento/Customer/Model/Plugin/CustomerAuthorization.php @@ -7,7 +7,10 @@ namespace Magento\Customer\Model\Plugin; use Magento\Authorization\Model\UserContextInterface; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\ResourceModel\Customer as CustomerResource; use Magento\Integration\Api\AuthorizationServiceInterface as AuthorizationService; +use Magento\Store\Model\StoreManagerInterface; /** * Plugin around \Magento\Framework\Authorization::isAllowed @@ -19,16 +22,41 @@ class CustomerAuthorization /** * @var UserContextInterface */ - protected $userContext; + private $userContext; + + /** + * @var CustomerFactory + */ + private $customerFactory; + + /** + * @var CustomerResource + */ + private $customerResource; + + /** + * @var StoreManagerInterface + */ + private $storeManager; /** * Inject dependencies. * * @param UserContextInterface $userContext + * @param CustomerFactory $customerFactory + * @param CustomerResource $customerResource + * @param StoreManagerInterface $storeManager */ - public function __construct(UserContextInterface $userContext) - { + public function __construct( + UserContextInterface $userContext, + CustomerFactory $customerFactory, + CustomerResource $customerResource, + StoreManagerInterface $storeManager + ) { $this->userContext = $userContext; + $this->customerFactory = $customerFactory; + $this->customerResource = $customerResource; + $this->storeManager = $storeManager; } /** @@ -53,9 +81,15 @@ public function aroundIsAllowed( && $this->userContext->getUserId() && $this->userContext->getUserType() === UserContextInterface::USER_TYPE_CUSTOMER ) { - return true; - } else { - return $proceed($resource, $privilege); + $customer = $this->customerFactory->create(); + $this->customerResource->load($customer, $this->userContext->getUserId()); + $currentStoreId = $this->storeManager->getStore()->getId(); + $sharedStoreIds = $customer->getSharedStoreIds(); + if (in_array($currentStoreId, $sharedStoreIds)) { + return true; + } } + + return $proceed($resource, $privilege); } } diff --git a/app/code/Magento/MediaContentCatalog/Observer/CategoryDelete.php b/app/code/Magento/MediaContentCatalog/Observer/CategoryDelete.php new file mode 100644 index 0000000000000..1565d455cc43f --- /dev/null +++ b/app/code/Magento/MediaContentCatalog/Observer/CategoryDelete.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentCatalog\Observer; + +use Magento\Catalog\Model\Category as CatalogCategory; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; +use Magento\MediaContentApi\Api\Data\ContentAssetLinkInterfaceFactory; +use Magento\MediaContentApi\Api\DeleteContentAssetLinksInterface; +use Magento\MediaContentApi\Model\GetEntityContentsInterface; +use Magento\MediaContentApi\Api\ExtractAssetsFromContentInterface; + +/** + * Observe the catalog_category_delete_after event and deletes relation between category content and media asset. + */ +class CategoryDelete implements ObserverInterface +{ + private const CONTENT_TYPE = 'catalog_category'; + private const TYPE = 'entityType'; + private const ENTITY_ID = 'entityId'; + private const FIELD = 'field'; + + /** + * @var ContentIdentityInterfaceFactory + */ + private $contentIdentityFactory; + + /** + * @var ContentAssetLinkInterfaceFactory + */ + private $contentAssetLinkFactory; + + /** + * @var DeleteContentAssetLinksInterface + */ + private $deleteContentAssetLinks; + + /** + * @var array + */ + private $fields; + + /** + * @var GetEntityContentsInterface + */ + private $getContent; + + /** + * @var ExtractAssetsFromContentInterface + */ + private $extractAssetsFromContent; + + /** + * @param ExtractAssetsFromContentInterface $extractAssetsFromContent + * @param GetEntityContentsInterface $getContent + * @param DeleteContentAssetLinksInterface $deleteContentAssetLinks + * @param ContentIdentityInterfaceFactory $contentIdentityFactory + * @param ContentAssetLinkInterfaceFactory $contentAssetLinkFactory + * @param array $fields + */ + public function __construct( + ExtractAssetsFromContentInterface $extractAssetsFromContent, + GetEntityContentsInterface $getContent, + DeleteContentAssetLinksInterface $deleteContentAssetLinks, + ContentIdentityInterfaceFactory $contentIdentityFactory, + ContentAssetLinkInterfaceFactory $contentAssetLinkFactory, + array $fields + ) { + $this->extractAssetsFromContent = $extractAssetsFromContent; + $this->getContent = $getContent; + $this->deleteContentAssetLinks = $deleteContentAssetLinks; + $this->contentAssetLinkFactory = $contentAssetLinkFactory; + $this->contentIdentityFactory = $contentIdentityFactory; + $this->fields = $fields; + } + + /** + * Retrieve the deleted category and remove relation betwen category and asset + * + * @param Observer $observer + * @throws \Exception + */ + public function execute(Observer $observer): void + { + $category = $observer->getEvent()->getData('category'); + $contentAssetLinks = []; + + if ($category instanceof CatalogCategory) { + foreach ($this->fields as $field) { + $contentIdentity = $this->contentIdentityFactory->create( + [ + self::TYPE => self::CONTENT_TYPE, + self::FIELD => $field, + self::ENTITY_ID => (string) $category->getEntityId(), + ] + ); + $content = implode(PHP_EOL, $this->getContent->execute($contentIdentity)); + $assets = $this->extractAssetsFromContent->execute($content); + + foreach ($assets as $asset) { + $contentAssetLinks[] = $this->contentAssetLinkFactory->create( + [ + 'assetId' => $asset->getId(), + 'contentIdentity' => $contentIdentity + ] + ); + } + } + if (!empty($contentAssetLinks)) { + $this->deleteContentAssetLinks->execute($contentAssetLinks); + } + } + } +} diff --git a/app/code/Magento/MediaContentCatalog/Observer/ProductDelete.php b/app/code/Magento/MediaContentCatalog/Observer/ProductDelete.php new file mode 100644 index 0000000000000..421bb5a33fa1d --- /dev/null +++ b/app/code/Magento/MediaContentCatalog/Observer/ProductDelete.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentCatalog\Observer; + +use Magento\Catalog\Model\Product as CatalogProduct; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; +use Magento\MediaContentApi\Api\Data\ContentAssetLinkInterfaceFactory; +use Magento\MediaContentApi\Api\DeleteContentAssetLinksInterface; +use Magento\MediaContentApi\Model\GetEntityContentsInterface; +use Magento\MediaContentApi\Api\ExtractAssetsFromContentInterface; + +/** + * Observe the catalog_product_delete_before event and deletes relation between category content and media asset. + */ +class ProductDelete implements ObserverInterface +{ + private const CONTENT_TYPE = 'catalog_product'; + private const TYPE = 'entityType'; + private const ENTITY_ID = 'entityId'; + private const FIELD = 'field'; + + /** + * @var ContentIdentityInterfaceFactory + */ + private $contentIdentityFactory; + + /** + * @var ContentAssetLinkInterfaceFactory + */ + private $contentAssetLinkFactory; + + /** + * @var DeleteContentAssetLinksInterface + */ + private $deleteContentAssetLinks; + + /** + * @var array + */ + private $fields; + + /** + * @var GetEntityContentsInterface + */ + private $getContent; + + /** + * @var ExtractAssetsFromContentInterface + */ + private $extractAssetsFromContent; + + /** + * @param ExtractAssetsFromContentInterface $extractAssetsFromContent + * @param GetEntityContentsInterface $getContent + * @param DeleteContentAssetLinksInterface $deleteContentAssetLinks + * @param ContentIdentityInterfaceFactory $contentIdentityFactory + * @param ContentAssetLinkInterfaceFactory $contentAssetLinkFactory + * @param array $fields + */ + public function __construct( + ExtractAssetsFromContentInterface $extractAssetsFromContent, + GetEntityContentsInterface $getContent, + DeleteContentAssetLinksInterface $deleteContentAssetLinks, + ContentIdentityInterfaceFactory $contentIdentityFactory, + ContentAssetLinkInterfaceFactory $contentAssetLinkFactory, + array $fields + ) { + $this->extractAssetsFromContent = $extractAssetsFromContent; + $this->getContent = $getContent; + $this->deleteContentAssetLinks = $deleteContentAssetLinks; + $this->contentAssetLinkFactory = $contentAssetLinkFactory; + $this->contentIdentityFactory = $contentIdentityFactory; + $this->fields = $fields; + } + + /** + * Retrieve the deleted product and remove relation betwen product and asset + * + * @param Observer $observer + * @throws \Exception + */ + public function execute(Observer $observer): void + { + $product = $observer->getEvent()->getData('product'); + $contentAssetLinks = []; + + if ($product instanceof CatalogProduct) { + foreach ($this->fields as $field) { + $contentIdentity = $this->contentIdentityFactory->create( + [ + self::TYPE => self::CONTENT_TYPE, + self::FIELD => $field, + self::ENTITY_ID => (string) $product->getEntityId(), + ] + ); + $productContent = implode(PHP_EOL, $this->getContent->execute($contentIdentity)); + $assets = $this->extractAssetsFromContent->execute($productContent); + + foreach ($assets as $asset) { + $contentAssetLinks[] = $this->contentAssetLinkFactory->create( + [ + 'assetId' => $asset->getId(), + 'contentIdentity' => $contentIdentity + ] + ); + } + } + if (!empty($contentAssetLinks)) { + $this->deleteContentAssetLinks->execute($contentAssetLinks); + } + } + } +} diff --git a/app/code/Magento/MediaContentCatalog/etc/di.xml b/app/code/Magento/MediaContentCatalog/etc/di.xml index a2d300a2bb208..6b0ee83b30788 100644 --- a/app/code/Magento/MediaContentCatalog/etc/di.xml +++ b/app/code/Magento/MediaContentCatalog/etc/di.xml @@ -14,6 +14,22 @@ </argument> </arguments> </type> + <type name="Magento\MediaContentCatalog\Observer\ProductDelete"> + <arguments> + <argument name="fields" xsi:type="array"> + <item name="description" xsi:type="string">description</item> + <item name="short_description" xsi:type="string">short_description</item> + </argument> + </arguments> + </type> + <type name="Magento\MediaContentCatalog\Observer\CategoryDelete"> + <arguments> + <argument name="fields" xsi:type="array"> + <item name="image" xsi:type="string">image</item> + <item name="description" xsi:type="string">description</item> + </argument> + </arguments> + </type> <type name="Magento\MediaContentCatalog\Observer\Category"> <arguments> <argument name="fields" xsi:type="array"> diff --git a/app/code/Magento/MediaContentCatalog/etc/events.xml b/app/code/Magento/MediaContentCatalog/etc/events.xml index f68d66eb3cc40..8ec7a30b961ba 100644 --- a/app/code/Magento/MediaContentCatalog/etc/events.xml +++ b/app/code/Magento/MediaContentCatalog/etc/events.xml @@ -9,6 +9,12 @@ <event name="catalog_category_save_after"> <observer name="media_content_catalog_category_save_after" instance="Magento\MediaContentCatalog\Observer\Category" /> </event> + <event name="catalog_product_delete_before"> + <observer name="media_content_catalog_product_delete_before" instance="Magento\MediaContentCatalog\Observer\ProductDelete" /> + </event> + <event name="catalog_category_delete_before"> + <observer name="media_content_catalog_category_delete_before" instance="Magento\MediaContentCatalog\Observer\CategoryDelete" /> + </event> <event name="catalog_product_save_after"> <observer name="media_content_catalog_product_save_after" instance="Magento\MediaContentCatalog\Observer\Product" /> </event> diff --git a/app/code/Magento/MediaContentCms/Observer/BlockDelete.php b/app/code/Magento/MediaContentCms/Observer/BlockDelete.php new file mode 100644 index 0000000000000..582f0a9ec6701 --- /dev/null +++ b/app/code/Magento/MediaContentCms/Observer/BlockDelete.php @@ -0,0 +1,119 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentCms\Observer; + +use Magento\Cms\Model\Block as CmsBlock; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; +use Magento\MediaContentApi\Api\Data\ContentAssetLinkInterfaceFactory; +use Magento\MediaContentApi\Api\DeleteContentAssetLinksInterface; +use Magento\MediaContentApi\Model\GetEntityContentsInterface; +use Magento\MediaContentApi\Api\ExtractAssetsFromContentInterface; + +/** + * Observe the adminhtml_cmspage_on_delete event and deletes relation between page content and media asset. + */ +class BlockDelete implements ObserverInterface +{ + private const CONTENT_TYPE = 'cms_block'; + private const TYPE = 'entityType'; + private const ENTITY_ID = 'entityId'; + private const FIELD = 'field'; + + /** + * @var ContentIdentityInterfaceFactory + */ + private $contentIdentityFactory; + + /** + * @var ContentAssetLinkInterfaceFactory + */ + private $contentAssetLinkFactory; + + /** + * @var DeleteContentAssetLinksInterface + */ + private $deleteContentAssetLinks; + + /** + * @var array + */ + private $fields; + + /** + * @var GetEntityContentsInterface + */ + private $getContent; + + /** + * @var ExtractAssetsFromContentInterface + */ + private $extractAssetsFromContent; + + /** + * @param ExtractAssetsFromContentInterface $extractAssetsFromContent + * @param GetEntityContentsInterface $getContent + * @param DeleteContentAssetLinksInterface $deleteContentAssetLinks + * @param ContentIdentityInterfaceFactory $contentIdentityFactory + * @param ContentAssetLinkInterfaceFactory $contentAssetLinkFactory + * @param array $fields + */ + public function __construct( + ExtractAssetsFromContentInterface $extractAssetsFromContent, + GetEntityContentsInterface $getContent, + DeleteContentAssetLinksInterface $deleteContentAssetLinks, + ContentIdentityInterfaceFactory $contentIdentityFactory, + ContentAssetLinkInterfaceFactory $contentAssetLinkFactory, + array $fields + ) { + $this->extractAssetsFromContent = $extractAssetsFromContent; + $this->getContent = $getContent; + $this->deleteContentAssetLinks = $deleteContentAssetLinks; + $this->contentAssetLinkFactory = $contentAssetLinkFactory; + $this->contentIdentityFactory = $contentIdentityFactory; + $this->fields = $fields; + } + + /** + * Retrieve the deleted category and remove relation betwen category and asset + * + * @param Observer $observer + * @throws \Exception + */ + public function execute(Observer $observer): void + { + $block = $observer->getEvent()->getData('object'); + $contentAssetLinks = []; + + if ($block instanceof CmsBlock) { + foreach ($this->fields as $field) { + $contentIdentity = $this->contentIdentityFactory->create( + [ + self::TYPE => self::CONTENT_TYPE, + self::FIELD => $field, + self::ENTITY_ID => (string) $block->getId(), + ] + ); + $assets = $this->extractAssetsFromContent->execute((string) $block->getData($field)); + + foreach ($assets as $asset) { + $contentAssetLinks[] = $this->contentAssetLinkFactory->create( + [ + 'assetId' => $asset->getId(), + 'contentIdentity' => $contentIdentity + ] + ); + } + } + if (!empty($contentAssetLinks)) { + $this->deleteContentAssetLinks->execute($contentAssetLinks); + } + } + } +} diff --git a/app/code/Magento/MediaContentCms/Observer/PageDelete.php b/app/code/Magento/MediaContentCms/Observer/PageDelete.php new file mode 100644 index 0000000000000..96d2bf89873bd --- /dev/null +++ b/app/code/Magento/MediaContentCms/Observer/PageDelete.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentCms\Observer; + +use Magento\Cms\Model\Page as CmsPage; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; +use Magento\MediaContentApi\Api\Data\ContentAssetLinkInterfaceFactory; +use Magento\MediaContentApi\Api\DeleteContentAssetLinksInterface; +use Magento\MediaContentApi\Model\GetEntityContentsInterface; +use Magento\MediaContentApi\Api\ExtractAssetsFromContentInterface; + +/** + * Observe the cms_page_delete_before event and deletes relation between page content and media asset. + */ +class PageDelete implements ObserverInterface +{ + private const CONTENT_TYPE = 'cms_page'; + private const TYPE = 'entityType'; + private const ENTITY_ID = 'entityId'; + private const FIELD = 'field'; + + /** + * @var ContentIdentityInterfaceFactory + */ + private $contentIdentityFactory; + + /** + * @var ContentAssetLinkInterfaceFactory + */ + private $contentAssetLinkFactory; + + /** + * @var DeleteContentAssetLinksInterface + */ + private $deleteContentAssetLinks; + + /** + * @var array + */ + private $fields; + + /** + * @var GetEntityContentsInterface + */ + private $getContent; + + /** + * @var ExtractAssetsFromContentInterface + */ + private $extractAssetsFromContent; + + /** + * @param ExtractAssetsFromContentInterface $extractAssetsFromContent + * @param GetEntityContentsInterface $getContent + * @param DeleteContentAssetLinksInterface $deleteContentAssetLinks + * @param ContentIdentityInterfaceFactory $contentIdentityFactory + * @param ContentAssetLinkInterfaceFactory $contentAssetLinkFactory + * @param arry $fields + */ + public function __construct( + ExtractAssetsFromContentInterface $extractAssetsFromContent, + GetEntityContentsInterface $getContent, + DeleteContentAssetLinksInterface $deleteContentAssetLinks, + ContentIdentityInterfaceFactory $contentIdentityFactory, + ContentAssetLinkInterfaceFactory $contentAssetLinkFactory, + array $fields + ) { + $this->extractAssetsFromContent = $extractAssetsFromContent; + $this->getContent = $getContent; + $this->deleteContentAssetLinks = $deleteContentAssetLinks; + $this->contentAssetLinkFactory = $contentAssetLinkFactory; + $this->contentIdentityFactory = $contentIdentityFactory; + $this->fields = $fields; + } + + /** + * Retrieve the deleted category and remove relation betwen category and asset + * + * @param Observer $observer + * @throws \Exception + */ + public function execute(Observer $observer): void + { + $page = $observer->getEvent()->getData('object'); + $contentAssetLinks = []; + + if ($page instanceof CmsPage) { + foreach ($this->fields as $field) { + $contentIdentity = $this->contentIdentityFactory->create( + [ + self::TYPE => self::CONTENT_TYPE, + self::FIELD => $field, + self::ENTITY_ID => (string) $page->getId(), + ] + ); + + $assets = $this->extractAssetsFromContent->execute((string) $page->getData($field)); + + foreach ($assets as $asset) { + $contentAssetLinks[] = $this->contentAssetLinkFactory->create( + [ + 'assetId' => $asset->getId(), + 'contentIdentity' => $contentIdentity + ] + ); + } + } + if (!empty($contentAssetLinks)) { + $this->deleteContentAssetLinks->execute($contentAssetLinks); + } + } + } +} diff --git a/app/code/Magento/MediaContentCms/etc/di.xml b/app/code/Magento/MediaContentCms/etc/di.xml index f980936465faf..6f196889540af 100644 --- a/app/code/Magento/MediaContentCms/etc/di.xml +++ b/app/code/Magento/MediaContentCms/etc/di.xml @@ -20,4 +20,18 @@ </argument> </arguments> </type> + <type name="Magento\MediaContentCms\Observer\PageDelete"> + <arguments> + <argument name="fields" xsi:type="array"> + <item name="content" xsi:type="string">content</item> + </argument> + </arguments> + </type> + <type name="Magento\MediaContentCms\Observer\BlockDelete"> + <arguments> + <argument name="fields" xsi:type="array"> + <item name="content" xsi:type="string">content</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/MediaContentCms/etc/events.xml b/app/code/Magento/MediaContentCms/etc/events.xml index 7e9abe3bf19c4..94f963f40be15 100644 --- a/app/code/Magento/MediaContentCms/etc/events.xml +++ b/app/code/Magento/MediaContentCms/etc/events.xml @@ -6,8 +6,14 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="cms_page_delete_before"> + <observer name="media_content_cms_page_delete_before" instance="Magento\MediaContentCms\Observer\PageDelete" /> + </event> <event name="cms_page_save_after"> <observer name="media_content_cms_page_save_after" instance="Magento\MediaContentCms\Observer\Page" /> + </event> + <event name="cms_block_delete_before"> + <observer name="media_content_cms_block_delete_before" instance="Magento\MediaContentCms\Observer\BlockDelete" /> </event> <event name="cms_block_save_after"> <observer name="media_content_cms_block_save_after" instance="Magento\MediaContentCms\Observer\Block" /> diff --git a/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml b/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml index 55798169cdf75..b42cabde6cd85 100644 --- a/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml @@ -14,7 +14,6 @@ <script> require([ 'jquery', - 'jquery-ui-modules/datepicker' ], function($){ //<![CDATA[ @@ -34,7 +33,7 @@ require([ timeText: "<?= $block->escapeJs(__('Time')) ?>", hourText: "<?= $block->escapeJs(__('Hour')) ?>", minuteText: "<?= $block->escapeJs(__('Minute')) ?>", - dateFormat: $.datepicker.RFC_2822, + dateFormat: "D, d M yy", // $.datepicker.RFC_2822 showOn: "button", showAnim: "", changeMonth: true, diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js index 2fab8c219c02a..284d395d8120b 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js @@ -7,11 +7,8 @@ define([ 'ko', 'underscore', 'jquery', - 'mage/translate', - 'mage/calendar', - 'moment', - 'mageUtils' -], function (ko, _, $, $t, calendar, moment, utils) { + 'mage/translate' +], function (ko, _, $, $t) { 'use strict'; var defaults = { @@ -46,10 +43,12 @@ define([ observable = config; } - $(el).calendar(options); + require(['mage/calendar'], function () { + $(el).calendar(options); - ko.utils.registerEventHandler(el, 'change', function () { - observable(this.value); + ko.utils.registerEventHandler(el, 'change', function () { + observable(this.value); + }); }); }, @@ -62,6 +61,7 @@ define([ */ update: function (element, valueAccessor) { var config = valueAccessor(), + $element = $(element), observable, options = {}, newVal; @@ -75,26 +75,21 @@ define([ observable = config; } - if (_.isEmpty(observable())) { - if ($(element).datepicker('getDate')) { - $(element).datepicker('setDate', null); - $(element).blur(); + require(['moment', 'mage/utils/misc', 'mage/calendar'], function (moment, utils) { + if (_.isEmpty(observable())) { + newVal = null; + } else { + newVal = moment( + observable(), + utils.convertToMomentFormat( + options.dateFormat + (options.showsTime ? ' ' + options.timeFormat : '') + ) + ).toDate(); } - } else { - newVal = moment( - observable(), - utils.convertToMomentFormat( - options.dateFormat + (options.showsTime ? ' ' + options.timeFormat : '') - ) - ).toDate(); - if ($(element).datepicker('getDate') == null || - newVal.valueOf() !== $(element).datepicker('getDate').valueOf() - ) { - $(element).datepicker('setDate', newVal); - $(element).blur(); - } - } + $element.datepicker('setDate', newVal); + $element.blur(); + }); } }; }); diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerSharingOptionsTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerSharingOptionsTest.php new file mode 100644 index 0000000000000..9c7abcd6c8364 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerSharingOptionsTest.php @@ -0,0 +1,213 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Api; + +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Registry; +use Magento\Framework\Webapi\Rest\Request; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Integration\Model\Oauth\Token as TokenModel; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Customer as CustomerHelper; +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Store/_files/second_website_with_two_stores.php + */ +class CustomerSharingOptionsTest extends WebapiAbstract +{ + const RESOURCE_PATH = '/V1/customers/me'; + const REPO_SERVICE = 'customerCustomerRepositoryV1'; + const SERVICE_VERSION = 'V1'; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var CustomerRegistry + */ + private $customerRegistry; + + /** + * @var CustomerHelper + */ + private $customerHelper; + + /** + * @var TokenModel + */ + private $token; + + /** + * @var CustomerInterface + */ + private $customerData; + + /** + * @var CustomerTokenServiceInterface + */ + private $tokenService; + + /** + * Execute per test initialization. + */ + public function setUp(): void + { + $this->customerRegistry = Bootstrap::getObjectManager()->get( + \Magento\Customer\Model\CustomerRegistry::class + ); + + $this->customerRepository = Bootstrap::getObjectManager()->get( + CustomerRepositoryInterface::class, + ['customerRegistry' => $this->customerRegistry] + ); + + $this->customerHelper = new CustomerHelper(); + $this->customerData = $this->customerHelper->createSampleCustomer(); + $this->tokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + + // get token + $this->resetTokenForCustomerSampleData(); + } + + /** + * Ensure that fixture customer and his addresses are deleted. + */ + public function tearDown(): void + { + $this->customerRepository = null; + + /** @var Registry $registry */ + $registry = Bootstrap::getObjectManager()->get(Registry::class); + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', true); + + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', false); + parent::tearDown(); + } + + /** + * @param string $storeCode + * @param bool $expectingException + * @dataProvider getCustomerDataWebsiteScopeDataProvider + * + * @magentoConfigFixture default_store customer/account_share/scope 1 + */ + public function testGetCustomerDataWebsiteScope(string $storeCode, bool $expectingException) + { + $this->_markTestAsRestOnly('SOAP is difficult to generate exception messages, inconsistencies in WSDL'); + $this->processGetCustomerData($storeCode, $expectingException); + } + + /** + * @param string $storeCode + * @param bool $expectingException + * @dataProvider getCustomerDataGlobalScopeDataProvider + * + * @magentoConfigFixture customer/account_share/scope 0 + */ + public function testGetCustomerDataGlobalScope(string $storeCode, bool $expectingException) + { + $this->processGetCustomerData($storeCode, $expectingException); + } + + /** + * @param string $storeCode + * @param bool $expectingException + */ + private function processGetCustomerData(string $storeCode, bool $expectingException) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => Request::HTTP_METHOD_GET, + 'token' => $this->token, + ], + 'soap' => [ + 'service' => self::REPO_SERVICE, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::REPO_SERVICE . 'GetSelf', + 'token' => $this->token + ] + ]; + $arguments = []; + if (TESTS_WEB_API_ADAPTER === 'soap') { + $arguments['customerId'] = 0; + } + + if ($expectingException) { + self::expectException(\Exception::class); + self::expectExceptionMessage("The consumer isn't authorized to access %resources."); + } + + $this->_webApiCall($serviceInfo, $arguments, null, $storeCode); + } + + /** + * Data provider for testGetCustomerDataWebsiteScope. + * + * @return array + */ + public function getCustomerDataWebsiteScopeDataProvider(): array + { + return [ + 'Default Store View' => [ + 'store_code' => 'default', + 'exception' => false + ], + 'Custom Store View' => [ + 'store_code' => 'fixture_second_store', + 'exception' => true + ] + ]; + } + + /** + * Data provider for testGetCustomerDataGlobalScope. + * + * @return array + */ + public function getCustomerDataGlobalScopeDataProvider(): array + { + return [ + 'Default Store View' => [ + 'store_code' => 'default', + 'exception' => false + ], + 'Custom Store View' => [ + 'store_code' => 'fixture_second_store', + 'exception' => false + ] + ]; + } + + /** + * Sets the test's access token for the created customer sample data + */ + private function resetTokenForCustomerSampleData() + { + $this->resetTokenForCustomer($this->customerData[CustomerInterface::EMAIL], 'test@123'); + } + + /** + * Sets the test's access token for a particular username and password. + * + * @param string $username + * @param string $password + */ + private function resetTokenForCustomer($username, $password) + { + $this->token = $this->tokenService->createCustomerAccessToken($username, $password); + $this->customerRegistry->remove($this->customerRepository->get($username)->getId()); + } +} diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js index 38728bca39192..a8ea949b52ebc 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js @@ -8,6 +8,7 @@ define([ 'jquery', 'moment', 'mageUtils', + 'mage/calendar', 'Magento_Ui/js/lib/knockout/bindings/datepicker' ], function (ko, $, moment, utils) { 'use strict'; @@ -18,6 +19,7 @@ define([ config; beforeEach(function () { + jasmine.clock().install(); element = $('<input />'); observable = ko.observable(); @@ -38,6 +40,7 @@ define([ }); afterEach(function () { + jasmine.clock().uninstall(); element.remove(); }); @@ -62,6 +65,8 @@ define([ expectedDate = moment(date, utils.convertToMomentFormat(inputFormat)).toDate(); observable(date); + jasmine.clock().tick(100); + expect(expectedDate.valueOf()).toEqual(element.datepicker('getDate').valueOf()); }); @@ -69,6 +74,8 @@ define([ element.datepicker('setTimezoneDate').blur().trigger('change'); observable(''); + jasmine.clock().tick(100); + expect(null).toEqual(element.datepicker('getDate')); }); }); diff --git a/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php b/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php index 3bd0677c6a443..3929c4cb47573 100644 --- a/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php +++ b/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php @@ -92,9 +92,31 @@ private function getMca() */ public function isCurrent() { + $urlByPath = preg_replace(self::REGEX_INDEX_URL_PATTERN, '', $this->getUrl($this->getPath())); return $this->getCurrent() || - preg_replace(self::REGEX_INDEX_URL_PATTERN, '', $this->getUrl($this->getPath())) - == preg_replace(self::REGEX_INDEX_URL_PATTERN, '', $this->getUrl($this->getMca())); + ($urlByPath == preg_replace(self::REGEX_INDEX_URL_PATTERN, '', $this->getUrl($this->getMca()))) || + $this->isCurrentCmsUrl($urlByPath); + } + + /** + * Get Current displayed page url + * + * @return string + */ + private function getCurrentUrl() + { + return $this->getUrl('*/*/*', ['_current' => false, '_use_rewrite' => true]); + } + + /** + * Check if link URL equivalent to URL of currently displayed CMS page + * + * @param string $urlByPath + * @return bool + */ + private function isCurrentCmsUrl($urlByPath) + { + return ($urlByPath == preg_replace(self::REGEX_INDEX_URL_PATTERN, '', $this->getCurrentUrl())); } /** diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php index b6bf31f580ab6..852cb901e430e 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php @@ -14,109 +14,159 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +/** + * @covers \Magento\Framework\View\Element\Html\Link\Current + */ class CurrentTest extends TestCase { /** - * @var MockObject + * @var UrlInterface|MockObject */ - protected $_urlBuilderMock; + private $_urlBuilderMock; /** - * @var MockObject + * @var Http|MockObject */ - protected $_requestMock; + private $_requestMock; /** - * @var ObjectManager + * @var Current */ - protected $_objectManager; + private $currentLink; + /** + * @inheritDoc + */ protected function setUp(): void { - $this->_objectManager = new ObjectManager($this); - $this->_urlBuilderMock = $this->getMockForAbstractClass(UrlInterface::class); + $this->_urlBuilderMock = $this->createMock(UrlInterface::class); $this->_requestMock = $this->createMock(Http::class); + + $this->currentLink = (new ObjectManager($this))->getObject( + Current::class, + [ + 'urlBuilder' => $this->_urlBuilderMock, + 'request' => $this->_requestMock + ] + ); } - public function testGetUrl() + /** + * Test get Url + */ + public function testGetUrl(): void { - $path = 'test/path'; - $url = 'http://example.com/asdasd'; + $pathStub = 'test/path'; + $urlStub = 'http://example.com/asdasd'; - $this->_urlBuilderMock->expects($this->once())->method('getUrl')->with($path)->willReturn($url); + $this->_urlBuilderMock->expects($this->once()) + ->method('getUrl') + ->with($pathStub) + ->will($this->returnValue($urlStub)); - /** @var Current $link */ - $link = $this->_objectManager->getObject( - Current::class, - ['urlBuilder' => $this->_urlBuilderMock] - ); + $this->currentLink->setPath($pathStub); - $link->setPath($path); - $this->assertEquals($url, $link->getHref()); + $this->assertEquals($urlStub, $this->currentLink->getHref()); } - public function testIsCurrentIfIsset() + /** + * Test if set current + */ + public function testIsCurrentIfIsset(): void { - /** @var Current $link */ - $link = $this->_objectManager->getObject(Current::class); - $link->setCurrent(true); - $this->assertTrue($link->isCurrent()); + $this->currentLink->setCurrent(true); + $this->assertTrue($this->currentLink->isCurrent()); } /** * Test if the current url is the same as link path * - * @return void + * @param string $pathStub + * @param string $urlStub + * @param array $request + * @param bool $expected + * @dataProvider isCurrentDataProvider */ - public function testIsCurrent() + public function testIsCurrent($pathStub, $urlStub, $request, $expected): void { - $path = 'test/index'; - $url = 'http://example.com/test/index'; - - $this->_requestMock->expects($this->once()) + $this->_requestMock->expects($this->any()) ->method('getPathInfo') - ->willReturn('/test/index/'); - $this->_requestMock->expects($this->once()) + ->will($this->returnValue($request['pathInfoStub'])); + $this->_requestMock->expects($this->any()) ->method('getModuleName') - ->willReturn('test'); - $this->_requestMock->expects($this->once()) + ->will($this->returnValue($request['moduleStub'])); + $this->_requestMock->expects($this->any()) ->method('getControllerName') - ->willReturn('index'); - $this->_requestMock->expects($this->once()) + ->will($this->returnValue($request['controllerStub'])); + $this->_requestMock->expects($this->any()) ->method('getActionName') - ->willReturn('index'); + ->will($this->returnValue($request['actionStub'])); + $this->_urlBuilderMock->expects($this->at(0)) ->method('getUrl') - ->with($path) - ->willReturn($url); + ->with($pathStub) + ->will($this->returnValue($urlStub)); $this->_urlBuilderMock->expects($this->at(1)) ->method('getUrl') - ->with('test/index') - ->willReturn($url); - - /** @var Current $link */ - $link = $this->_objectManager->getObject( - Current::class, - [ - 'urlBuilder' => $this->_urlBuilderMock, - 'request' => $this->_requestMock - ] - ); - - $link->setPath($path); - $this->assertTrue($link->isCurrent()); + ->with($request['mcaStub']) + ->will($this->returnValue($request['getUrl'])); + + if ($request['mcaStub'] == '') { + $this->_urlBuilderMock->expects($this->at(2)) + ->method('getUrl') + ->with('*/*/*', ['_current' => false, '_use_rewrite' => true]) + ->will($this->returnValue($urlStub)); + } + + $this->currentLink->setPath($pathStub); + $this->assertEquals($expected, $this->currentLink->isCurrent()); } - public function testIsCurrentFalse() + /** + * Data provider for is current + */ + public function isCurrentDataProvider(): array { - $this->_urlBuilderMock->expects($this->at(0))->method('getUrl')->willReturn('1'); - $this->_urlBuilderMock->expects($this->at(1))->method('getUrl')->willReturn('2'); - - /** @var Current $link */ - $link = $this->_objectManager->getObject( - Current::class, - ['urlBuilder' => $this->_urlBuilderMock, 'request' => $this->_requestMock] - ); - $this->assertFalse($link->isCurrent()); + return [ + 'url with MCA' => [ + 'pathStub' => 'test/path', + 'urlStub' => 'http://example.com/asdasd', + 'requestStub' => [ + 'pathInfoStub' => '/test/index/', + 'moduleStub' => 'test', + 'controllerStub' => 'index', + 'actionStub' => 'index', + 'mcaStub' => 'test/index', + 'getUrl' => 'http://example.com/asdasd/' + ], + 'excepted' => true + ], + 'url with CMS' => [ + 'pathStub' => 'test', + 'urlStub' => 'http://example.com/test', + 'requestStub' => [ + 'pathInfoStub' => '//test//', + 'moduleStub' => 'cms', + 'controllerStub' => 'page', + 'actionStub' => 'view', + 'mcaStub' => '', + 'getUrl' => 'http://example.com/' + ], + 'excepted' => true + ], + 'Test if is current false' => [ + 'pathStub' => 'test/path', + 'urlStub' => 'http://example.com/tests', + 'requestStub' => [ + 'pathInfoStub' => '/test/index/', + 'moduleStub' => 'test', + 'controllerStub' => 'index', + 'actionStub' => 'index', + 'mcaStub' => 'test/index', + 'getUrl' => 'http://example.com/asdasd/' + ], + 'excepted' => false + ] + ]; } }