diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml index 9716791985970..36347e59a3d6b 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml @@ -68,14 +68,9 @@ - - - - - - + diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml index ef84ebd6fafea..24ac3db27175c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml @@ -118,12 +118,7 @@ - - - - - - + diff --git a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php index aeef8d00f720a..a3ae616dfcf4f 100644 --- a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php +++ b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php @@ -6,21 +6,24 @@ namespace Magento\CacheInvalidate\Model; use Magento\Framework\Cache\InvalidateLogger; +use Magento\PageCache\Model\Cache\Server; +use Laminas\Http\Client\Adapter\Socket; +use Laminas\Uri\Uri; /** - * PurgeCache model + * Invalidate external HTTP cache(s) based on tag pattern */ class PurgeCache { const HEADER_X_MAGENTO_TAGS_PATTERN = 'X-Magento-Tags-Pattern'; /** - * @var \Magento\PageCache\Model\Cache\Server + * @var Server */ protected $cacheServer; /** - * @var \Magento\CacheInvalidate\Model\SocketFactory + * @var SocketFactory */ protected $socketAdapterFactory; @@ -39,39 +42,46 @@ class PurgeCache * * @var int */ - private $requestSize = 7680; + private $maxHeaderSize; /** * Constructor * - * @param \Magento\PageCache\Model\Cache\Server $cacheServer - * @param \Magento\CacheInvalidate\Model\SocketFactory $socketAdapterFactory + * @param Server $cacheServer + * @param SocketFactory $socketAdapterFactory * @param InvalidateLogger $logger + * @param int $maxHeaderSize */ public function __construct( - \Magento\PageCache\Model\Cache\Server $cacheServer, - \Magento\CacheInvalidate\Model\SocketFactory $socketAdapterFactory, - InvalidateLogger $logger + Server $cacheServer, + SocketFactory $socketAdapterFactory, + InvalidateLogger $logger, + int $maxHeaderSize = 7680 ) { $this->cacheServer = $cacheServer; $this->socketAdapterFactory = $socketAdapterFactory; $this->logger = $logger; + $this->maxHeaderSize = $maxHeaderSize; } /** * Send curl purge request to invalidate cache by tags pattern * - * @param string $tagsPattern + * @param array|string $tags * @return bool Return true if successful; otherwise return false */ - public function sendPurgeRequest($tagsPattern) + public function sendPurgeRequest($tags) { + if (is_string($tags)) { + $tags = [$tags]; + } + $successful = true; $socketAdapter = $this->socketAdapterFactory->create(); $servers = $this->cacheServer->getUris(); $socketAdapter->setOptions(['timeout' => 10]); - $formattedTagsChunks = $this->splitTags($tagsPattern); + $formattedTagsChunks = $this->chunkTags($tags); foreach ($formattedTagsChunks as $formattedTagsChunk) { if (!$this->sendPurgeRequestToServers($socketAdapter, $servers, $formattedTagsChunk)) { $successful = false; @@ -82,24 +92,24 @@ public function sendPurgeRequest($tagsPattern) } /** - * Split tags by batches + * Split tags into batches to suit Varnish max. header size * - * @param string $tagsPattern + * @param array $tags * @return \Generator */ - private function splitTags($tagsPattern) + private function chunkTags(array $tags): \Generator { - $tagsBatchSize = 0; + $currentBatchSize = 0; $formattedTagsChunk = []; - $formattedTags = explode('|', $tagsPattern); - foreach ($formattedTags as $formattedTag) { - if ($tagsBatchSize + strlen($formattedTag) > $this->requestSize - count($formattedTagsChunk) - 1) { + foreach ($tags as $formattedTag) { + // Check if (currentBatchSize + length of next tag + number of pipe delimiters) would exceed header size. + if ($currentBatchSize + strlen($formattedTag) + count($formattedTagsChunk) > $this->maxHeaderSize) { yield implode('|', $formattedTagsChunk); $formattedTagsChunk = []; - $tagsBatchSize = 0; + $currentBatchSize = 0; } - $tagsBatchSize += strlen($formattedTag); + $currentBatchSize += strlen($formattedTag); $formattedTagsChunk[] = $formattedTag; } if (!empty($formattedTagsChunk)) { @@ -110,12 +120,12 @@ private function splitTags($tagsPattern) /** * Send curl purge request to servers to invalidate cache by tags pattern * - * @param \Laminas\Http\Client\Adapter\Socket $socketAdapter - * @param \Laminas\Uri\Uri[] $servers + * @param Socket $socketAdapter + * @param Uri[] $servers * @param string $formattedTagsChunk * @return bool Return true if successful; otherwise return false */ - private function sendPurgeRequestToServers($socketAdapter, $servers, $formattedTagsChunk) + private function sendPurgeRequestToServers(Socket $socketAdapter, array $servers, string $formattedTagsChunk): bool { $headers = [self::HEADER_X_MAGENTO_TAGS_PATTERN => $formattedTagsChunk]; $unresponsiveServerError = []; @@ -145,14 +155,14 @@ private function sendPurgeRequestToServers($socketAdapter, $servers, $formattedT if ($errorCount == count($servers)) { $this->logger->critical( 'No cache server(s) could be purged ' . $loggerMessage, - compact('server', 'formattedTagsChunk') + compact('servers', 'formattedTagsChunk') ); return false; } $this->logger->warning( 'Unresponsive cache server(s) hit' . $loggerMessage, - compact('server', 'formattedTagsChunk') + compact('servers', 'formattedTagsChunk') ); } diff --git a/app/code/Magento/CacheInvalidate/Observer/FlushAllCacheObserver.php b/app/code/Magento/CacheInvalidate/Observer/FlushAllCacheObserver.php index 97e11e8bd2f3f..574263ca1ee0c 100644 --- a/app/code/Magento/CacheInvalidate/Observer/FlushAllCacheObserver.php +++ b/app/code/Magento/CacheInvalidate/Observer/FlushAllCacheObserver.php @@ -7,6 +7,9 @@ use Magento\Framework\Event\ObserverInterface; +/** + * Clear configured Varnish hosts when triggering a full cache flush (e.g. from the Cache Management admin dashboard) + */ class FlushAllCacheObserver implements ObserverInterface { /** @@ -43,7 +46,7 @@ public function __construct( public function execute(\Magento\Framework\Event\Observer $observer) { if ($this->config->getType() == \Magento\PageCache\Model\Config::VARNISH && $this->config->isEnabled()) { - $this->purgeCache->sendPurgeRequest('.*'); + $this->purgeCache->sendPurgeRequest(['.*']); } } } diff --git a/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php b/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php index a3062ea9d6a76..7dba4393d3973 100644 --- a/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php +++ b/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php @@ -77,7 +77,7 @@ public function execute(Observer $observer) $tags[] = sprintf($pattern, $tag); } if (!empty($tags)) { - $this->purgeCache->sendPurgeRequest(implode('|', array_unique($tags))); + $this->purgeCache->sendPurgeRequest(array_unique($tags)); } } } diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php index c88d8f5266fb8..97c3acc194842 100644 --- a/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php +++ b/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php @@ -53,6 +53,7 @@ protected function setUp(): void 'cacheServer' => $this->cacheServer, 'socketAdapterFactory' => $socketFactoryMock, 'logger' => $this->loggerMock, + 'maxHeaderSize' => 256 ] ); } @@ -64,6 +65,7 @@ protected function setUp(): void public function testSendPurgeRequest($hosts) { $uris = []; + /** @var array $host */ foreach ($hosts as $host) { $port = isset($host['port']) ? $host['port'] : Server::DEFAULT_PORT; $uris[] = UriFactory::factory('')->setHost($host['host']) @@ -92,7 +94,50 @@ public function testSendPurgeRequest($hosts) $this->loggerMock->expects($this->once()) ->method('execute'); - $this->assertTrue($this->model->sendPurgeRequest('tags')); + $this->assertTrue($this->model->sendPurgeRequest(['tags'])); + } + + public function testSendMultiPurgeRequest() + { + $tags = [ + '(^|,)cat_p_95(,|$)', '(^|,)cat_p_96(,|$)', '(^|,)cat_p_97(,|$)', '(^|,)cat_p_98(,|$)', + '(^|,)cat_p_99(,|$)', '(^|,)cat_p_100(,|$)', '(^|,)cat_p_10038(,|$)', '(^|,)cat_p_142985(,|$)', + '(^|,)cat_p_199(,|$)', '(^|,)cat_p_300(,|$)', '(^|,)cat_p_12038(,|$)', '(^|,)cat_p_152985(,|$)', + '(^|,)cat_p_299(,|$)', '(^|,)cat_p_400(,|$)', '(^|,)cat_p_13038(,|$)', '(^|,)cat_p_162985(,|$)', + ]; + + $tagsSplitA = array_slice($tags, 0, 12); + $tagsSplitB = array_slice($tags, 12, 4); + + $uri = UriFactory::factory('')->setHost('localhost') + ->setPort(80) + ->setScheme('http'); + + $this->cacheServer->expects($this->once()) + ->method('getUris') + ->willReturn([$uri]); + + $this->socketAdapterMock->expects($this->exactly(2)) + ->method('connect') + ->with($uri->getHost(), $uri->getPort()); + + $this->socketAdapterMock->expects($this->exactly(2)) + ->method('write') + ->withConsecutive( + [ + 'PURGE', $uri, '1.1', + ['X-Magento-Tags-Pattern' => implode('|', $tagsSplitA), 'Host' => $uri->getHost()] + ], + [ + 'PURGE', $uri, '1.1', + ['X-Magento-Tags-Pattern' => implode('|', $tagsSplitB), 'Host' => $uri->getHost()] + ] + ); + + $this->socketAdapterMock->expects($this->exactly(2)) + ->method('close'); + + $this->assertTrue($this->model->sendPurgeRequest($tags)); } /** @@ -130,6 +175,6 @@ public function testSendPurgeRequestWithException() $this->loggerMock->expects($this->once()) ->method('critical'); - $this->assertFalse($this->model->sendPurgeRequest('tags')); + $this->assertFalse($this->model->sendPurgeRequest(['tags'])); } } diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Observer/FlushAllCacheObserverTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Observer/FlushAllCacheObserverTest.php index 3db36e4c0e3e9..544e23288d720 100644 --- a/app/code/Magento/CacheInvalidate/Test/Unit/Observer/FlushAllCacheObserverTest.php +++ b/app/code/Magento/CacheInvalidate/Test/Unit/Observer/FlushAllCacheObserverTest.php @@ -56,7 +56,7 @@ public function testFlushAllCache() Config::VARNISH ); - $this->purgeCache->expects($this->once())->method('sendPurgeRequest')->with('.*'); + $this->purgeCache->expects($this->once())->method('sendPurgeRequest')->with(['.*']); $this->model->execute($this->observerMock); } } diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php index d550cd7ea9487..38319dcc724ca 100644 --- a/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php +++ b/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php @@ -82,7 +82,7 @@ protected function setUp(): void public function testInvalidateVarnish() { $tags = ['cache_1', 'cache_group']; - $pattern = '((^|,)cache_1(,|$))|((^|,)cache_group(,|$))'; + $pattern = ['((^|,)cache_1(,|$))', '((^|,)cache_group(,|$))']; $this->configMock->expects($this->once())->method('isEnabled')->willReturn(true); $this->configMock->expects( diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductsFromGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductsFromGridActionGroup.xml new file mode 100644 index 0000000000000..d5c4f97c88da2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductsFromGridActionGroup.xml @@ -0,0 +1,21 @@ + + + + + + + Select and delete products in product grid. + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml index 0565f2d08cc1f..fea4436446da2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml @@ -18,19 +18,24 @@ + - + + - + + - + + - + + - + diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php index bf1d3772b92a0..1ad82497119ba 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php @@ -8,12 +8,14 @@ namespace Magento\CatalogImportExport\Test\Unit\Model\Export; use Magento\Catalog\Model\Product\LinkTypeProvider; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; use Magento\CatalogImportExport\Model\Export\Product; use Magento\CatalogImportExport\Model\Export\Product\Type\Factory; use Magento\CatalogImportExport\Model\Export\RowCustomizer\Composite; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Collection\AbstractCollection; use Magento\Eav\Model\Entity\Type; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory as AttributeSetCollectionFactory; use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Logger\Monolog; @@ -83,7 +85,7 @@ class ProductTest extends TestCase protected $attrSetColFactory; /** - * @var \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory|MockObject + * @var CategoryCollectionFactory|MockObject */ protected $categoryColFactory; @@ -174,15 +176,14 @@ protected function setUp(): void ->onlyMethods(['create']) ->getMock(); - $this->attrSetColFactory = $this->getMockBuilder( - \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory::class - )->addMethods(['setEntityTypeFilter']) + $this->attrSetColFactory = $this->getMockBuilder(AttributeSetCollectionFactory::class) + ->disableOriginalConstructor() + ->addMethods(['setEntityTypeFilter']) ->onlyMethods(['create']) ->getMock(); - $this->categoryColFactory = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory::class - )->addMethods(['addNameToResult']) + $this->categoryColFactory = $this->getMockBuilder(CategoryCollectionFactory::class) + ->disableOriginalConstructor()->addMethods(['addNameToResult']) ->onlyMethods(['create']) ->getMock(); diff --git a/app/code/Magento/CatalogInventory/Model/StockState.php b/app/code/Magento/CatalogInventory/Model/StockState.php index ac6dc16366798..e28a2096942d4 100644 --- a/app/code/Magento/CatalogInventory/Model/StockState.php +++ b/app/code/Magento/CatalogInventory/Model/StockState.php @@ -9,8 +9,11 @@ use Magento\CatalogInventory\Api\StockStateInterface; use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; +use Magento\Framework\DataObject; /** + * Provides functionality for stock state information + * * Interface StockState */ class StockState implements StockStateInterface @@ -31,6 +34,8 @@ class StockState implements StockStateInterface protected $stockConfiguration; /** + * StockState constructor + * * @param StockStateProviderInterface $stockStateProvider * @param StockRegistryProviderInterface $stockRegistryProvider * @param StockConfigurationInterface $stockConfiguration @@ -46,30 +51,32 @@ public function __construct( } /** + * Verify stock by product id + * * @param int $productId * @param int $scopeId * @return bool */ public function verifyStock($productId, $scopeId = null) { - // if ($scopeId === null) { - $scopeId = $this->stockConfiguration->getDefaultScopeId(); - // } + $scopeId = $this->stockConfiguration->getDefaultScopeId(); $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); + return $this->stockStateProvider->verifyStock($stockItem); } /** + * Verify notification by product id + * * @param int $productId * @param int $scopeId * @return bool */ public function verifyNotification($productId, $scopeId = null) { - // if ($scopeId === null) { - $scopeId = $this->stockConfiguration->getDefaultScopeId(); - // } + $scopeId = $this->stockConfiguration->getDefaultScopeId(); $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); + return $this->stockStateProvider->verifyNotification($stockItem); } @@ -84,16 +91,14 @@ public function verifyNotification($productId, $scopeId = null) */ public function checkQty($productId, $qty, $scopeId = null) { - // if ($scopeId === null) { - $scopeId = $this->stockConfiguration->getDefaultScopeId(); - // } + $scopeId = $this->stockConfiguration->getDefaultScopeId(); $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); + return $this->stockStateProvider->checkQty($stockItem, $qty); } /** - * Returns suggested qty that satisfies qty increments and minQty/maxQty/minSaleQty/maxSaleQty conditions - * or original qty if such value does not exist + * Returns suggested qty that satisfies qty increments/minQty/maxQty/minSaleQty/maxSaleQty else returns original qty * * @param int $productId * @param float $qty @@ -102,10 +107,9 @@ public function checkQty($productId, $qty, $scopeId = null) */ public function suggestQty($productId, $qty, $scopeId = null) { - // if ($scopeId === null) { - $scopeId = $this->stockConfiguration->getDefaultScopeId(); - // } + $scopeId = $this->stockConfiguration->getDefaultScopeId(); $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); + return $this->stockStateProvider->suggestQty($stockItem, $qty); } @@ -118,29 +122,31 @@ public function suggestQty($productId, $qty, $scopeId = null) */ public function getStockQty($productId, $scopeId = null) { - // if ($scopeId === null) { - $scopeId = $this->stockConfiguration->getDefaultScopeId(); - // } + $scopeId = $this->stockConfiguration->getDefaultScopeId(); $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); + return $this->stockStateProvider->getStockQty($stockItem); } /** + * Check qty increments by product id + * * @param int $productId * @param float $qty * @param int $websiteId - * @return \Magento\Framework\DataObject + * @return DataObject */ public function checkQtyIncrements($productId, $qty, $websiteId = null) { - // if ($websiteId === null) { - $websiteId = $this->stockConfiguration->getDefaultScopeId(); - // } + $websiteId = $this->stockConfiguration->getDefaultScopeId(); $stockItem = $this->stockRegistryProvider->getStockItem($productId, $websiteId); + return $this->stockStateProvider->checkQtyIncrements($stockItem, $qty); } /** + * Check quote item qty + * * @param int $productId * @param float $itemQty * @param float $qtyToCheck @@ -150,10 +156,9 @@ public function checkQtyIncrements($productId, $qty, $websiteId = null) */ public function checkQuoteItemQty($productId, $itemQty, $qtyToCheck, $origQty, $scopeId = null) { - // if ($scopeId === null) { - $scopeId = $this->stockConfiguration->getDefaultScopeId(); - // } + $scopeId = $this->stockConfiguration->getDefaultScopeId(); $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); + return $this->stockStateProvider->checkQuoteItemQty($stockItem, $itemQty, $qtyToCheck, $origQty); } } diff --git a/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php b/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php index 7fa0fbfa44b0a..0e918c23857ac 100644 --- a/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php +++ b/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php @@ -96,6 +96,8 @@ protected function _getChildrenElementsHtml(AbstractElement $element) . '' . $field->toHtml() . ''; } else { $elements .= $field->toHtml(); + $styleTag = $this->addVisibilityTag($field); + $elements .= $styleTag; } } @@ -168,11 +170,13 @@ protected function _getFrontendClass($element) */ protected function _getHeaderTitleHtml($element) { + $styleTag = $this->addVisibilityTag($element); return '' . $element->getLegend() . '' . + $styleTag . /* @noEscape */ $this->secureRenderer->renderEventListenerAsTag( 'onclick', 'event.preventDefault();' . @@ -270,10 +274,70 @@ protected function _isCollapseState($element) return true; } + if ($this->isCollapseStateByDependentField($element)) { + return false; + } + $extra = $this->_authSession->getUser()->getExtra(); + if (isset($extra['configState'][$element->getId()])) { return $extra['configState'][$element->getId()]; } return $this->isCollapsedDefault; } + + /** + * Check if element should be collapsed by dependent field value. + * + * @param AbstractElement $element + * @return bool + */ + private function isCollapseStateByDependentField(AbstractElement $element): bool + { + if (!empty($element->getGroup()['depends']['fields'])) { + foreach ($element->getGroup()['depends']['fields'] as $dependFieldData) { + if (is_array($dependFieldData) && isset($dependFieldData['value'], $dependFieldData['id'])) { + $fieldSetForm = $this->getForm(); + $dependentFieldConfigValue = $this->_scopeConfig->getValue( + $dependFieldData['id'], + $fieldSetForm->getScope(), + $fieldSetForm->getScopeCode() + ); + + if ($dependFieldData['value'] !== $dependentFieldConfigValue) { + return true; + } + } + } + } + + return false; + } + + /** + * If element or it's parent depends on other element we hide it during page load. + * + * @param AbstractElement $field + * @return string + */ + private function addVisibilityTag(AbstractElement $field): string + { + $elementId = ''; + $styleTag = ''; + + if (!empty($field->getFieldConfig()['depends']['fields'])) { + $elementId = '#row_' . $field->getHtmlId(); + } elseif (!empty($field->getGroup()['depends']['fields'])) { + $elementId = '#' . $field->getHtmlId() . '-head'; + } + + if (!empty($elementId)) { + $styleTag .= $this->secureRenderer->renderStyleAsTag( + 'display: none;', + $elementId + ); + } + + return $styleTag; + } } diff --git a/app/code/Magento/Customer/Block/Account/Navigation.php b/app/code/Magento/Customer/Block/Account/Navigation.php index dcbd637ac360e..9a8aa698eaa47 100644 --- a/app/code/Magento/Customer/Block/Account/Navigation.php +++ b/app/code/Magento/Customer/Block/Account/Navigation.php @@ -47,6 +47,6 @@ public function getLinks() */ private function compare(SortLinkInterface $firstLink, SortLinkInterface $secondLink): int { - return $firstLink->getSortOrder() <=> $secondLink->getSortOrder(); + return $secondLink->getSortOrder() <=> $firstLink->getSortOrder(); } } diff --git a/app/code/Magento/Customer/Model/Options.php b/app/code/Magento/Customer/Model/Options.php index c230353f2a284..4c9b9f97ad43a 100644 --- a/app/code/Magento/Customer/Model/Options.php +++ b/app/code/Magento/Customer/Model/Options.php @@ -97,14 +97,15 @@ private function prepareNamePrefixSuffixOptions($options, $isOptional = false) if (empty($options)) { return false; } + $result = []; - $options = array_filter(explode(';', $options)); + $options = explode(';', $options); foreach ($options as $value) { - $value = $this->escaper->escapeHtml(trim($value)); - $result[$value] = $value; + $result[] = $this->escaper->escapeHtml(trim($value)) ?: ' '; } + if ($isOptional && trim(current($options))) { - $result = array_merge([' ' => ' '], $result); + $result = array_merge([' '], $result); } return $result; diff --git a/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php b/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php index 0dc375908e561..c93e7110c5d96 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php @@ -18,11 +18,6 @@ class NavigationTest extends TestCase { - /** - * Stub name top links - */ - private const STUB_TOP_LINKS_NAME_IN_LAYOUT = 'top.links'; - /** * @var ObjectManagerHelper */ @@ -67,7 +62,7 @@ protected function setUp(): void * * @return void */ - public function testGetLinksWithCustomerAndWishList(): void + public function testGetLinksWithCustomerAndWishList() { $wishListLinkMock = $this->getMockBuilder(WishListLink::class) ->disableOriginalConstructor() @@ -81,30 +76,30 @@ public function testGetLinksWithCustomerAndWishList(): void $wishListLinkMock->expects($this->any()) ->method('getSortOrder') - ->willReturn(30); + ->willReturn(100); $customerAccountLinkMock->expects($this->any()) ->method('getSortOrder') - ->willReturn(0); + ->willReturn(20); - $topLinksNameInLayout = self::STUB_TOP_LINKS_NAME_IN_LAYOUT; + $nameInLayout = 'top.links'; $blockChildren = [ - 'customerAccountLink' => $customerAccountLinkMock, - 'wishListLink' => $wishListLinkMock + 'wishListLink' => $wishListLinkMock, + 'customerAccountLink' => $customerAccountLinkMock ]; - $this->navigation->setNameInLayout($topLinksNameInLayout); + $this->navigation->setNameInLayout($nameInLayout); $this->layoutMock->expects($this->any()) ->method('getChildBlocks') - ->with($topLinksNameInLayout) + ->with($nameInLayout) ->willReturn($blockChildren); /* Assertion */ $this->assertEquals( [ - 0 => $customerAccountLinkMock, - 1 => $wishListLinkMock + 0 => $wishListLinkMock, + 1 => $customerAccountLinkMock ], $this->navigation->getLinks() ); diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_account.xml b/app/code/Magento/Customer/view/frontend/layout/customer_account.xml index 260f35e3cf444..a2a15a4166b73 100644 --- a/app/code/Magento/Customer/view/frontend/layout/customer_account.xml +++ b/app/code/Magento/Customer/view/frontend/layout/customer_account.xml @@ -24,31 +24,31 @@ My Account customer/account - 1 + 250 - 40 + 200 Address Book customer/address - 50 + 190 Account Information customer/account/edit - 60 + 180 - 90 + 130 diff --git a/app/code/Magento/Customer/view/frontend/layout/default.xml b/app/code/Magento/Customer/view/frontend/layout/default.xml index 3c6be7a9ee5fe..3976fc6bd9090 100644 --- a/app/code/Magento/Customer/view/frontend/layout/default.xml +++ b/app/code/Magento/Customer/view/frontend/layout/default.xml @@ -8,10 +8,10 @@ - + My Account - 1 + 110 @@ -20,11 +20,7 @@ - - 30 - - + template="Magento_Customer::account/link/authorization.phtml"/> diff --git a/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php b/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php index a9e42cd01f2d5..152bdfef376fb 100644 --- a/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php +++ b/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php @@ -46,7 +46,7 @@ public function testExecuteBadType() ->with( $this->equalTo(['urn:magento:framework:Module/etc/module.xsd' => $fixtureXmlFile]), $this->equalTo('test') - )->willReturn(null); + ); $formats = ['phpstorm' => $phpstormFormatMock]; $readFactory = $this->createMock(ReadFactory::class); diff --git a/app/code/Magento/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php b/app/code/Magento/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php index dbf8eacbd7848..5a54a274485fe 100644 --- a/app/code/Magento/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php +++ b/app/code/Magento/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php @@ -12,7 +12,7 @@ use Magento\Store\Model\ScopeInterface; /** - * Downlaodable Sales Order Email items renderer + * Downloadable Sales Order Email items renderer * * @api * @since 100.0.2 @@ -81,6 +81,8 @@ public function getLinks() } /** + * Returns links title + * * @return null|string */ public function getLinksTitle() @@ -92,6 +94,8 @@ public function getLinksTitle() } /** + * Returns purchased link url + * * @param Item $item * @return string */ diff --git a/app/code/Magento/Downloadable/view/frontend/layout/customer_account.xml b/app/code/Magento/Downloadable/view/frontend/layout/customer_account.xml index b86d05d20afaa..5ce45a27615e0 100644 --- a/app/code/Magento/Downloadable/view/frontend/layout/customer_account.xml +++ b/app/code/Magento/Downloadable/view/frontend/layout/customer_account.xml @@ -12,7 +12,7 @@ downloadable/customer/products My Downloadable Products - 20 + 217 diff --git a/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php b/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php index f0f4eba026733..547be1de5a008 100644 --- a/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php +++ b/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php @@ -13,6 +13,8 @@ /** * Pop-up for Login as Customer button then Login as Customer is not allowed. + * + * @api */ class NotAllowedPopup extends Template { diff --git a/app/code/Magento/MediaGallery/Model/ResourceModel/Keyword/SaveAssetLinks.php b/app/code/Magento/MediaGallery/Model/ResourceModel/Keyword/SaveAssetLinks.php index 95a0cadd3abcc..eb6bd2aad236c 100644 --- a/app/code/Magento/MediaGallery/Model/ResourceModel/Keyword/SaveAssetLinks.php +++ b/app/code/Magento/MediaGallery/Model/ResourceModel/Keyword/SaveAssetLinks.php @@ -24,6 +24,7 @@ class SaveAssetLinks private const TABLE_ASSET_KEYWORD = 'media_gallery_asset_keyword'; private const FIELD_ASSET_ID = 'asset_id'; private const FIELD_KEYWORD_ID = 'keyword_id'; + private const TABLE_MEDIA_ASSET = 'media_gallery_asset'; /** * @var ResourceConnection @@ -73,6 +74,10 @@ public function execute(int $assetId, array $keywordIds): void $this->deleteAssetKeywords($assetId, $obsoleteKeywordIds); $this->insertAssetKeywords($assetId, $newKeywordIds); + + if ($obsoleteKeywordIds || $newKeywordIds) { + $this->setAssetUpdatedAt($assetId); + } } /** @@ -179,4 +184,28 @@ function (KeywordInterface $keyword): int { $keywordsData ); } + + /** + * Updates modified date of media asset + * + * @param int $assetId + * @throws CouldNotSaveException + */ + private function setAssetUpdatedAt(int $assetId): void + { + try { + $connection = $this->resourceConnection->getConnection(); + $connection->update( + $connection->getTableName(self::TABLE_MEDIA_ASSET), + ['updated_at' => null], + ['id =?' => $assetId] + ); + } catch (\Exception $exception) { + $this->logger->critical($exception); + throw new CouldNotSaveException( + __('Could not update assets modified date'), + $exception + ); + } + } } diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/ResourceModel/Keyword/SaveAssetLinksTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/ResourceModel/Keyword/SaveAssetLinksTest.php index 0bc81fbedaa19..d027f0ed21b53 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/ResourceModel/Keyword/SaveAssetLinksTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/ResourceModel/Keyword/SaveAssetLinksTest.php @@ -75,7 +75,7 @@ public function testAssetKeywordsSave(int $assetId, array $keywordIds, array $va $expectedCalls = (int) (count($keywordIds)); if ($expectedCalls) { - $this->resourceConnectionMock->expects($this->once()) + $this->resourceConnectionMock->expects($this->exactly(2)) ->method('getConnection') ->willReturn($this->connectionMock); $this->resourceConnectionMock->expects($this->once()) diff --git a/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Columns/Path.php b/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Columns/Path.php index 38569f5f698da..f780a116baf9e 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Columns/Path.php +++ b/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Columns/Path.php @@ -6,6 +6,8 @@ namespace Magento\MediaGalleryCatalogUi\Ui\Component\Listing\Columns; use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Listing\Columns\Column; @@ -63,13 +65,15 @@ public function prepareDataSource(array $dataSource) * Replace category path ids with category names * * @param string $pathWithIds + * @return string + * @throws NoSuchEntityException */ private function getCategoryPathWithNames(string $pathWithIds): string { $categoryPathWithName = ''; $categoryIds = explode('/', $pathWithIds); foreach ($categoryIds as $id) { - if ($id == 1) { + if ($id == Category::TREE_ROOT_ID) { continue; } $categoryName = $this->categoryRepository->get($id)->getName(); diff --git a/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Filters/UsedInProducts.php b/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Filters/UsedInProducts.php new file mode 100644 index 0000000000000..254ebd047c954 --- /dev/null +++ b/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Filters/UsedInProducts.php @@ -0,0 +1,133 @@ +uiComponentFactory = $uiComponentFactory; + $this->filterBuilder = $filterBuilder; + parent::__construct( + $context, + $uiComponentFactory, + $filterBuilder, + $filterModifier, + $optionsProvider, + $components, + $data + ); + $this->bookmarkManagement = $bookmarkManagement; + $this->productRepository = $productRepository; + } + + /** + * Prepare component configuration + * + * @return void + */ + public function prepare() + { + $options = []; + $productIds = []; + $bookmarks = $this->bookmarkManagement->loadByNamespace($this->context->getNameSpace())->getItems(); + foreach ($bookmarks as $bookmark) { + if ($bookmark->getIdentifier() === 'current') { + $applied = $bookmark->getConfig()['current']['filters']['applied']; + if (isset($applied[$this->getName()])) { + $productIds = $applied[$this->getName()]; + } + } + } + + foreach ($productIds as $id) { + $product = $this->productRepository->getById($id); + $options[] = [ + 'value' => $id, + 'label' => $product->getName(), + 'is_active' => $product->getStatus(), + 'path' => $product->getSku(), + 'optgroup' => false + + ]; + } + + $this->wrappedComponent = $this->uiComponentFactory->create( + $this->getName(), + parent::COMPONENT, + [ + 'context' => $this->getContext(), + 'options' => $options + ] + ); + + $this->wrappedComponent->prepare(); + $productsFilterJsConfig = array_replace_recursive( + $this->getJsConfig($this->wrappedComponent), + $this->getJsConfig($this) + ); + $this->setData('js_config', $productsFilterJsConfig); + + $this->setData( + 'config', + array_replace_recursive( + (array)$this->wrappedComponent->getData('config'), + (array)$this->getData('config') + ) + ); + + $this->applyFilter(); + + parent::prepare(); + } +} diff --git a/app/code/Magento/MediaGalleryCatalogUi/etc/adminhtml/di.xml b/app/code/Magento/MediaGalleryCatalogUi/etc/adminhtml/di.xml index 500ac10f4745a..ae01c29928b4a 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/etc/adminhtml/di.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/etc/adminhtml/di.xml @@ -24,7 +24,7 @@ catalog_category - + diff --git a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_listing.xml b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_listing.xml index 97743b458e8d7..2ca58b6020fa7 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_listing.xml @@ -13,6 +13,7 @@ name="product_id" provider="${ $.parentName }" sortOrder="110" + class="Magento\MediaGalleryCatalogUi\Ui\Component\Listing\Filters\UsedInProducts" component="Magento_Catalog/js/components/product-ui-select" template="ui/grid/filters/elements/ui-select"> diff --git a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml index 97743b458e8d7..2ca58b6020fa7 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml @@ -13,6 +13,7 @@ name="product_id" provider="${ $.parentName }" sortOrder="110" + class="Magento\MediaGalleryCatalogUi\Ui\Component\Listing\Filters\UsedInProducts" component="Magento_Catalog/js/components/product-ui-select" template="ui/grid/filters/elements/ui-select"> diff --git a/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInBlocks.php b/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInBlocks.php new file mode 100644 index 0000000000000..09fea24c8a2a9 --- /dev/null +++ b/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInBlocks.php @@ -0,0 +1,131 @@ +uiComponentFactory = $uiComponentFactory; + $this->filterBuilder = $filterBuilder; + parent::__construct( + $context, + $uiComponentFactory, + $filterBuilder, + $filterModifier, + $optionsProvider, + $components, + $data + ); + $this->bookmarkManagement = $bookmarkManagement; + $this->blockRepository = $blockRepository; + } + + /** + * Prepare component configuration + * + * @return void + */ + public function prepare() + { + $options = []; + $blockIds = []; + $bookmarks = $this->bookmarkManagement->loadByNamespace($this->context->getNameSpace())->getItems(); + foreach ($bookmarks as $bookmark) { + if ($bookmark->getIdentifier() === 'current') { + $applied = $bookmark->getConfig()['current']['filters']['applied']; + if (isset($applied[$this->getName()])) { + $blockIds = $applied[$this->getName()]; + } + } + } + + foreach ($blockIds as $id) { + $block = $this->blockRepository->getById($id); + $options[] = [ + 'value' => $id, + 'label' => $block->getTitle(), + 'is_active' => $block->isActive(), + 'optgroup' => false + ]; + } + + $this->wrappedComponent = $this->uiComponentFactory->create( + $this->getName(), + parent::COMPONENT, + [ + 'context' => $this->getContext(), + 'options' => $options + ] + ); + + $this->wrappedComponent->prepare(); + $jsConfig = array_replace_recursive( + $this->getJsConfig($this->wrappedComponent), + $this->getJsConfig($this) + ); + $this->setData('js_config', $jsConfig); + + $this->setData( + 'config', + array_replace_recursive( + (array)$this->wrappedComponent->getData('config'), + (array)$this->getData('config') + ) + ); + + $this->applyFilter(); + + parent::prepare(); + } +} diff --git a/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInPages.php b/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInPages.php new file mode 100644 index 0000000000000..235a77cdcb8c5 --- /dev/null +++ b/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInPages.php @@ -0,0 +1,131 @@ +uiComponentFactory = $uiComponentFactory; + $this->filterBuilder = $filterBuilder; + parent::__construct( + $context, + $uiComponentFactory, + $filterBuilder, + $filterModifier, + $optionsProvider, + $components, + $data + ); + $this->bookmarkManagement = $bookmarkManagement; + $this->pageRepository = $pageRepository; + } + + /** + * Prepare component configuration + * + * @return void + */ + public function prepare() + { + $options = []; + $pageIds = []; + $bookmarks = $this->bookmarkManagement->loadByNamespace($this->context->getNameSpace())->getItems(); + foreach ($bookmarks as $bookmark) { + if ($bookmark->getIdentifier() === 'current') { + $applied = $bookmark->getConfig()['current']['filters']['applied']; + if (isset($applied[$this->getName()])) { + $pageIds = $applied[$this->getName()]; + } + } + } + + foreach ($pageIds as $id) { + $page = $this->pageRepository->getById($id); + $options[] = [ + 'value' => $id, + 'label' => $page->getTitle(), + 'is_active' => $page->isActive(), + 'optgroup' => false + ]; + } + + $this->wrappedComponent = $this->uiComponentFactory->create( + $this->getName(), + parent::COMPONENT, + [ + 'context' => $this->getContext(), + 'options' => $options + ] + ); + + $this->wrappedComponent->prepare(); + $pagesFilterjsConfig = array_replace_recursive( + $this->getJsConfig($this->wrappedComponent), + $this->getJsConfig($this) + ); + $this->setData('js_config', $pagesFilterjsConfig); + + $this->setData( + 'config', + array_replace_recursive( + (array)$this->wrappedComponent->getData('config'), + (array)$this->getData('config') + ) + ); + + $this->applyFilter(); + + parent::prepare(); + } +} diff --git a/app/code/Magento/MediaGalleryCmsUi/composer.json b/app/code/Magento/MediaGalleryCmsUi/composer.json index 1ecfb9a3c8855..73747a669c051 100644 --- a/app/code/Magento/MediaGalleryCmsUi/composer.json +++ b/app/code/Magento/MediaGalleryCmsUi/composer.json @@ -5,7 +5,8 @@ "php": "~7.3.0||~7.4.0", "magento/framework": "*", "magento/module-cms": "*", - "magento/module-backend": "*" + "magento/module-backend": "*", + "magento/module-ui": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/MediaGalleryCmsUi/etc/adminhtml/di.xml b/app/code/Magento/MediaGalleryCmsUi/etc/adminhtml/di.xml index b06ad0fff1df6..65ed3b7197f83 100644 --- a/app/code/Magento/MediaGalleryCmsUi/etc/adminhtml/di.xml +++ b/app/code/Magento/MediaGalleryCmsUi/etc/adminhtml/di.xml @@ -24,7 +24,7 @@ cms_block - + diff --git a/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/media_gallery_listing.xml b/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/media_gallery_listing.xml index 509a7e6a53673..506a6cad5b68e 100644 --- a/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/media_gallery_listing.xml @@ -13,6 +13,7 @@ name="page_id" provider="${ $.parentName }" sortOrder="120" + class="Magento\MediaGalleryCmsUi\Ui\Component\Listing\Filters\UsedInPages" component="Magento_Ui/js/form/element/ui-select" template="ui/grid/filters/elements/ui-select"> @@ -37,6 +38,7 @@ name="block_id" provider="${ $.parentName }" sortOrder="130" + class="Magento\MediaGalleryCmsUi\Ui\Component\Listing\Filters\UsedInBlocks" component="Magento_Ui/js/form/element/ui-select" template="ui/grid/filters/elements/ui-select"> diff --git a/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml b/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml index 509a7e6a53673..506a6cad5b68e 100644 --- a/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml @@ -13,6 +13,7 @@ name="page_id" provider="${ $.parentName }" sortOrder="120" + class="Magento\MediaGalleryCmsUi\Ui\Component\Listing\Filters\UsedInPages" component="Magento_Ui/js/form/element/ui-select" template="ui/grid/filters/elements/ui-select"> @@ -37,6 +38,7 @@ name="block_id" provider="${ $.parentName }" sortOrder="130" + class="Magento\MediaGalleryCmsUi\Ui\Component\Listing\Filters\UsedInBlocks" component="Magento_Ui/js/form/element/ui-select" template="ui/grid/filters/elements/ui-select"> diff --git a/app/code/Magento/MediaGallerySynchronization/Console/Command/Synchronize.php b/app/code/Magento/MediaGallerySynchronization/Console/Command/Synchronize.php index 992f77a1df319..339aca84ec68f 100644 --- a/app/code/Magento/MediaGallerySynchronization/Console/Command/Synchronize.php +++ b/app/code/Magento/MediaGallerySynchronization/Console/Command/Synchronize.php @@ -7,8 +7,6 @@ namespace Magento\MediaGallerySynchronization\Console\Command; -use Magento\Framework\App\Area; -use Magento\Framework\App\State; use Magento\Framework\Console\Cli; use Magento\MediaGallerySynchronizationApi\Api\SynchronizeInterface; use Symfony\Component\Console\Command\Command; @@ -25,21 +23,13 @@ class Synchronize extends Command */ private $synchronizeAssets; - /** - * @var State $state - */ - private $state; - /** * @param SynchronizeInterface $synchronizeAssets - * @param State $state */ public function __construct( - SynchronizeInterface $synchronizeAssets, - State $state + SynchronizeInterface $synchronizeAssets ) { $this->synchronizeAssets = $synchronizeAssets; - $this->state = $state; parent::__construct(); } @@ -60,10 +50,11 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $output->writeln('Synchronizing assets information from media storage to database...'); - $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () { - $this->synchronizeAssets->execute(); - }); + + $this->synchronizeAssets->execute(); + $output->writeln('Completed assets synchronization.'); + return Cli::RETURN_SUCCESS; } } diff --git a/app/code/Magento/MediaGallerySynchronization/Test/Integration/Model/SynchronizeFilesTest.php b/app/code/Magento/MediaGallerySynchronization/Test/Integration/Model/SynchronizeFilesTest.php new file mode 100644 index 0000000000000..6c4338c0935dc --- /dev/null +++ b/app/code/Magento/MediaGallerySynchronization/Test/Integration/Model/SynchronizeFilesTest.php @@ -0,0 +1,151 @@ +driver = Bootstrap::getObjectManager()->get(DriverInterface::class); + $this->synchronizeFiles = Bootstrap::getObjectManager()->get(SynchronizeFilesInterface::class); + $this->getAssetsByPath = Bootstrap::getObjectManager()->get(GetAssetsByPathsInterface::class); + $this->getAssetKeywords = Bootstrap::getObjectManager()->get(GetAssetsKeywordsInterface::class); + $this->mediaDirectory = Bootstrap::getObjectManager()->get(Filesystem::class) + ->getDirectoryWrite(DirectoryList::MEDIA); + } + + /** + * Test for SynchronizeFiles::execute + * + * @dataProvider filesProvider + * @param null|string $file + * @param null|string $title + * @param null|string $description + * @param null|array $keywords + * @throws FileSystemException + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testExecute( + ?string $file, + ?string $title, + ?string $description, + ?array $keywords + ): void { + $path = realpath(__DIR__ . '/../_files/' . $file); + $modifiableFilePath = $this->mediaDirectory->getAbsolutePath($file); + $this->driver->copy( + $path, + $modifiableFilePath + ); + + $this->synchronizeFiles->execute([$file]); + + $loadedAssets = $this->getAssetsByPath->execute([$file])[0]; + $loadedKeywords = $this->getKeywords($loadedAssets) ?: null; + + $this->assertEquals($title, $loadedAssets->getTitle()); + $this->assertEquals($description, $loadedAssets->getDescription()); + $this->assertEquals($keywords, $loadedKeywords); + + $this->driver->deleteFile($modifiableFilePath); + } + + /** + * Data provider for testExecute + * + * @return array[] + */ + public function filesProvider(): array + { + return [ + [ + '/magento.jpg', + 'magento', + null, + null + ], + [ + '/magento_metadata.jpg', + 'Title of the magento image', + 'Description of the magento image', + [ + 'magento', + 'mediagallerymetadata' + ] + ] + ]; + } + + /** + * Key asset keywords + * + * @param AssetInterface $asset + * @return string[] + */ + private function getKeywords(AssetInterface $asset): array + { + $assetKeywords = $this->getAssetKeywords->execute([$asset->getId()]); + + if (empty($assetKeywords)) { + return []; + } + + $keywords = current($assetKeywords)->getKeywords(); + + return array_map( + function (KeywordInterface $keyword) { + return $keyword->getKeyword(); + }, + $keywords + ); + } +} diff --git a/app/code/Magento/MediaGallerySynchronization/Test/Integration/_files/magento_metadata.jpg b/app/code/Magento/MediaGallerySynchronization/Test/Integration/_files/magento_metadata.jpg new file mode 100644 index 0000000000000..6dc8cd69e41c1 Binary files /dev/null and b/app/code/Magento/MediaGallerySynchronization/Test/Integration/_files/magento_metadata.jpg differ diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php index b4b6713f47065..df13250eacb5f 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php @@ -137,7 +137,7 @@ public function execute() if (!empty($assets)) { foreach ($assets as $asset) { $responseContent['options'][] = [ - 'value' => $asset->getId(), + 'value' => (string) $asset->getId(), 'label' => $asset->getTitle(), 'path' => $this->storage->getThumbnailUrl($this->images->getStorageRoot() . $asset->getPath()) ]; diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/CreatedAt.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/CreatedAt.php deleted file mode 100644 index 7c3eccfea521f..0000000000000 --- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/CreatedAt.php +++ /dev/null @@ -1,59 +0,0 @@ -dateTime = $dateTime; - } - - /** - * Provide asset created at date time - * - * @param AssetInterface $asset - * @return array - * @throws \Exception - */ - public function execute(AssetInterface $asset): array - { - return [ - 'title' => __('Created'), - 'value' => $this->formatDate($asset->getCreatedAt()) - ]; - } - - /** - * Format date to standard format - * - * @param string $date - * @return string - * @throws \Exception - */ - private function formatDate(string $date): string - { - return $this->dateTime->formatDate($date, \IntlDateFormatter::SHORT, true); - } -} diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Height.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Height.php deleted file mode 100644 index b2b0f389f6b9a..0000000000000 --- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Height.php +++ /dev/null @@ -1,33 +0,0 @@ - __('Height'), - 'value' => sprintf('%spx', $asset->getHeight()) - ]; - } -} diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Size.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Size.php deleted file mode 100644 index 55841cc5abd3f..0000000000000 --- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Size.php +++ /dev/null @@ -1,49 +0,0 @@ - __('Size'), - 'value' => $this->formatImageSize($asset->getSize()) - ]; - } - - /** - * Format image size - * - * @param int $imageSize - * - * @return string - */ - private function formatImageSize(int $imageSize): string - { - if ($imageSize === 0) { - return ''; - } - - return sprintf('%sKb', $imageSize / 1000); - } -} diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Type.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Type.php deleted file mode 100644 index 5b47616398ef7..0000000000000 --- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Type.php +++ /dev/null @@ -1,59 +0,0 @@ -types = $types; - } - - /** - * Provide asset type - * - * @param AssetInterface $asset - * @return array - * @throws IntegrationException - */ - public function execute(AssetInterface $asset): array - { - return [ - 'title' => __('Type'), - 'value' => $this->getImageTypeByContentType($asset->getContentType()), - ]; - } - - /** - * Return image type by content type - * - * @param string $contentType - * @return string - */ - private function getImageTypeByContentType(string $contentType): string - { - $type = current(explode('/', $contentType)); - - return isset($this->types[$type]) ? $this->types[$type] : 'Asset'; - } -} diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UpdatedAt.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UpdatedAt.php deleted file mode 100644 index 2f50bd9a72208..0000000000000 --- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UpdatedAt.php +++ /dev/null @@ -1,59 +0,0 @@ -dateTime = $dateTime; - } - - /** - * Provide asset updated at date time - * - * @param AssetInterface $asset - * @return array - * @throws \Exception - */ - public function execute(AssetInterface $asset): array - { - return [ - 'title' => __('Modified'), - 'value' => $this->formatDate($asset->getUpdatedAt()) - ]; - } - - /** - * Format date to standard format - * - * @param string $date - * @return string - * @throws \Exception - */ - private function formatDate(string $date): string - { - return $this->dateTime->formatDate($date, \IntlDateFormatter::SHORT, true); - } -} diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UsedIn.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UsedIn.php deleted file mode 100644 index ca3883d5c937c..0000000000000 --- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UsedIn.php +++ /dev/null @@ -1,113 +0,0 @@ -getContent = $getContent; - $this->url = $url; - $this->contentTypes = $contentTypes; - } - - /** - * Provide information on which content asset is used in - * - * @param AssetInterface $asset - * @return array - * @throws IntegrationException - */ - public function execute(AssetInterface $asset): array - { - return [ - 'title' => __('Used In'), - 'value' => $this->getUsedIn($asset->getId()) - ]; - } - - /** - * Retrieve assets used in the Content - * - * @param int $assetId - * @return array - * @throws IntegrationException - */ - private function getUsedIn(int $assetId): array - { - $details = []; - - foreach ($this->getUsedInCounts($assetId) as $type => $number) { - $details[$type] = $this->contentTypes[$type] ?? ['name' => $type, 'link' => null]; - $details[$type]['number'] = $number; - $details[$type]['link'] = $details[$type]['link'] ? $this->url->getUrl($details[$type]['link']) : null; - } - - return array_values($details); - } - - /** - * Get used in counts per type - * - * @param int $assetId - * @return int[] - * @throws IntegrationException - */ - private function getUsedInCounts(int $assetId): array - { - $usedIn = []; - $entityIds = []; - - $contentIdentities = $this->getContent->execute([$assetId]); - - foreach ($contentIdentities as $contentIdentity) { - $entityId = $contentIdentity->getEntityId(); - $type = $contentIdentity->getEntityType(); - - if (!isset($entityIds[$type])) { - $usedIn[$type] = 1; - } elseif ($entityIds[$type]['entity_id'] !== $entityId) { - ++$usedIn[$type]; - } - $entityIds[$type]['entity_id'] = $entityId; - } - return $usedIn; - } -} diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Width.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Width.php deleted file mode 100644 index 64e9cf8ad1a8f..0000000000000 --- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Width.php +++ /dev/null @@ -1,33 +0,0 @@ - __('Width'), - 'value' => sprintf('%spx', $asset->getWidth()) - ]; - } -} diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProviderInterface.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProviderInterface.php deleted file mode 100644 index 92375adfdd4f2..0000000000000 --- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProviderInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -detailsProviders = $detailsProviders; - } - - /** - * Get a piece of asset details - * - * @param AssetInterface $asset - * @return array - */ - public function execute(AssetInterface $asset): array - { - $details = []; - foreach ($this->detailsProviders as $detailsProvider) { - if ($detailsProvider instanceof AssetDetailsProviderInterface) { - $details[] = $detailsProvider->execute($asset); - } - } - return $details; - } -} diff --git a/app/code/Magento/MediaGalleryUi/Model/GetAssetDetails.php b/app/code/Magento/MediaGalleryUi/Model/GetAssetDetails.php new file mode 100644 index 0000000000000..88bd5cf96e534 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Model/GetAssetDetails.php @@ -0,0 +1,102 @@ +dateTime = $dateTime; + $this->getAssetUsageDetails = $getAssetUsageDetails; + } + + /** + * Get a piece of asset details + * + * @param AssetInterface $asset + * @return array + */ + public function execute(AssetInterface $asset): array + { + $details = [ + [ + 'title' => __('Type'), + 'value' => __('Image'), + ], + [ + 'title' => __('Created'), + 'value' => $this->formatDate($asset->getCreatedAt()) + ], + [ + 'title' => __('Modified'), + 'value' => $this->formatDate($asset->getUpdatedAt()) + ], + [ + 'title' => __('Width'), + 'value' => sprintf('%spx', $asset->getWidth()) + ], + [ + 'title' => __('Height'), + 'value' => sprintf('%spx', $asset->getHeight()) + ], + [ + 'title' => __('Size'), + 'value' => $this->formatSize($asset->getSize()) + ], + [ + 'title' => __('Used In'), + 'value' => $this->getAssetUsageDetails->execute($asset->getId()) + ] + ]; + return $details; + } + + /** + * Format image size + * + * @param int $size + * @return string + */ + private function formatSize(int $size): string + { + return $size === 0 ? '' : sprintf('%.2f KB', $size / 1024); + } + + /** + * Format date to standard format + * + * @param string $date + * @return string + */ + private function formatDate(string $date): string + { + return $this->dateTime->formatDate($date, \IntlDateFormatter::SHORT, true); + } +} diff --git a/app/code/Magento/MediaGalleryUi/Model/GetAssetUsageDetails.php b/app/code/Magento/MediaGalleryUi/Model/GetAssetUsageDetails.php new file mode 100644 index 0000000000000..1dd8b736a9c90 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Model/GetAssetUsageDetails.php @@ -0,0 +1,118 @@ +getContent = $getContent; + $this->url = $url; + $this->contentTypes = $contentTypes; + } + + /** + * Provide information on which content asset is used in + * + * @param int $id + * @return array + * @throws IntegrationException + */ + public function execute(int $id): array + { + $details = []; + + foreach ($this->getUsageByEntities($id) as $type => $entities) { + $details[] = [ + 'name' => $this->getName($type), + 'number' => count($entities), + 'link' => $this->getLinkUrl($type) + ]; + } + + return $details; + } + + /** + * Retrieve the type name from content types configuration + * + * @param string $type + * @return string + */ + private function getName(string $type): string + { + if (isset($this->contentTypes[$type]) && !empty($this->contentTypes[$type]['name'])) { + return $this->contentTypes[$type]['name']; + } + return $type; + } + + /** + * Retrieve the type link from content types configuration + * + * @param string $type + * @return string|null + */ + private function getLinkUrl(string $type): ?string + { + if (isset($this->contentTypes[$type]) && !empty($this->contentTypes[$type]['link'])) { + return $this->url->getUrl($this->contentTypes[$type]['link']); + } + return null; + } + + /** + * Get used in counts per type + * + * @param int $assetId + * @return int[] + * @throws IntegrationException + */ + private function getUsageByEntities(int $assetId): array + { + $usage = []; + + foreach ($this->getContent->execute([$assetId]) as $contentIdentity) { + $id = $contentIdentity->getEntityId(); + $type = $contentIdentity->getEntityType(); + $usage[$type][$id] = isset($usage[$type][$id]) ? $usage[$type][$id]++ : 0; + } + + return $usage; + } +} diff --git a/app/code/Magento/MediaGalleryUi/Model/GetDetailsByAssetId.php b/app/code/Magento/MediaGalleryUi/Model/GetDetailsByAssetId.php index b870082ea2aa1..f6972637b3610 100644 --- a/app/code/Magento/MediaGalleryUi/Model/GetDetailsByAssetId.php +++ b/app/code/Magento/MediaGalleryUi/Model/GetDetailsByAssetId.php @@ -44,25 +44,25 @@ class GetDetailsByAssetId private $getAssetKeywords; /** - * @var AssetDetailsProviderPool + * @var GetAssetDetails */ - private $detailsProviderPool; + private $getAssetDetails; /** - * @param AssetDetailsProviderPool $detailsProviderPool + * @param GetAssetDetails $getAssetDetails * @param GetAssetsByIdsInterface $getAssetById * @param StoreManagerInterface $storeManager * @param SourceIconProvider $sourceIconProvider * @param GetAssetsKeywordsInterface $getAssetKeywords */ public function __construct( - AssetDetailsProviderPool $detailsProviderPool, + GetAssetDetails $getAssetDetails, GetAssetsByIdsInterface $getAssetById, StoreManagerInterface $storeManager, SourceIconProvider $sourceIconProvider, GetAssetsKeywordsInterface $getAssetKeywords ) { - $this->detailsProviderPool = $detailsProviderPool; + $this->getAssetDetails = $getAssetDetails; $this->getAssetsById = $getAssetById; $this->storeManager = $storeManager; $this->sourceIconProvider = $sourceIconProvider; @@ -89,7 +89,7 @@ public function execute(array $assetIds): array 'path' => $asset->getPath(), 'description' => $asset->getDescription(), 'id' => $asset->getId(), - 'details' => $this->detailsProviderPool->execute($asset), + 'details' => $this->getAssetDetails->execute($asset), 'size' => $asset->getSize(), 'tags' => $this->getKeywords($asset), 'source' => $asset->getSource() ? diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryEnableMassActionModeActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryEnableMassActionModeActionGroup.xml index 4b0375088509b..5e5c89637c6a1 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryEnableMassActionModeActionGroup.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryEnableMassActionModeActionGroup.xml @@ -13,7 +13,7 @@ Activate massaction mode by click on Delete Selected.. - - + + diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryMassActionSection.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryMassActionSection.xml index a40a70c5f160c..07f2dc23530e1 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryMassActionSection.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryMassActionSection.xml @@ -11,6 +11,7 @@ - + + diff --git a/app/code/Magento/MediaGalleryUi/etc/di.xml b/app/code/Magento/MediaGalleryUi/etc/di.xml index 56ccf7c1aa727..a8c4e2a8d8963 100644 --- a/app/code/Magento/MediaGalleryUi/etc/di.xml +++ b/app/code/Magento/MediaGalleryUi/etc/di.xml @@ -33,27 +33,7 @@ media - - - - Image - - - - - - - Magento\MediaGalleryUi\Model\AssetDetailsProvider\Type - Magento\MediaGalleryUi\Model\AssetDetailsProvider\CreatedAt - Magento\MediaGalleryUi\Model\AssetDetailsProvider\UpdatedAt - Magento\MediaGalleryUi\Model\AssetDetailsProvider\Width - Magento\MediaGalleryUi\Model\AssetDetailsProvider\Height - Magento\MediaGalleryUi\Model\AssetDetailsProvider\Size - Magento\MediaGalleryUi\Model\AssetDetailsProvider\UsedIn - - - diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less index 671a82dce3f58..fc8bd49126d8e 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less @@ -29,7 +29,8 @@ } } - .media-gallery-asset-ui-select-filter { + .media-gallery-asset-ui-select-filter, + .edit-image-details { .admin__action-multiselect-crumb { max-width: 70%; @@ -47,7 +48,10 @@ position: absolute; text-overflow: ellipsis; } + } + .media-gallery-asset-ui-select-filter, + .edit-image-details { .admin__action-multiselect-item-path { float: right; max-height: 70px; @@ -307,7 +311,7 @@ } } - .adobe-stock-icon { + .media-gallery-source-icon { margin-bottom: -6px; width: 29px; } @@ -366,7 +370,7 @@ } .image-type { - .adobe-stock-icon { + .media-gallery-source-icon { margin-bottom: -6px; width: 29px; } @@ -412,12 +416,14 @@ & > * { float: left; + margin-left: -1px; width: 50%; } .value { display: inline; float: right; + margin-left: 1px; } .title { diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js index e7c05573a4f11..bf852d0ddae68 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js @@ -103,6 +103,7 @@ define([ */ updateSelected: function () { this.selected({}); + this.hideAddSelectedAndDeleteButon(); }, /** diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactionView.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactionView.js index a49303669edc8..ddc5af0ab6296 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactionView.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactionView.js @@ -7,33 +7,23 @@ define([ 'jquery', 'uiComponent', 'mage/translate', - 'text!Magento_MediaGalleryUi/template/grid/massactions/cancelButton.html' -], function ($, Component, $t, cancelMassActionButton) { + 'text!Magento_MediaGalleryUi/template/grid/massactions/massactionButtons.html' +], function ($, Component, $t, massactionButtons) { 'use strict'; return Component.extend({ defaults: { - pageActionsSelector: '.page-actions-buttons', gridSelector: '[data-id="media-gallery-masonry-grid"]', - originDeleteSelector: null, - originCancelEvent: null, - cancelMassactionButton: cancelMassActionButton, - isCancelButtonInserted: false, - deleteButtonSelector: '#delete_massaction', - addSelectedButtonSelector: '#add_selected', - cancelMassactionButtonSelector: '#cancel', standAloneTitle: 'Manage Gallery', slidePanelTitle: 'Media Gallery', defaultTitle: null, - contextButtonSelector: '.three-dots', - buttonsIds: [ - '#delete_folder', - '#create_folder', - '#upload_image', - '#search_adobe_stock', - '.three-dots', - '#add_selected' - ], + are: null, + standAloneArea: 'standalone', + slidepanelArea: 'slidepanel', + massactionButtonsSelector: '.massaction-buttons', + buttonsSelectorStandalone: '.page-actions-buttons', + buttonsSelectorSlidePanel: '.page-actions.floating-header', + buttons: '.page-main-actions :button', massactionModeTitle: $t('Select Images to Delete') }, @@ -62,7 +52,6 @@ define([ * Hide or show buttons per active mode. */ switchButtons: function () { - if (this.massActionMode()) { this.activateMassactionButtonView(); } else { @@ -74,58 +63,24 @@ define([ * Sets buttons to default regular -mode view. */ revertButtonsToDefaultView: function () { - $(this.deleteButtonSelector).replaceWith(this.originDeleteSelector); - - if (!this.isCancelButtonInserted) { - $('#cancel_massaction').replaceWith(this.originCancelEvent); - } else { - $(this.cancelMassactionButtonSelector).addClass('no-display'); - $('#cancel_massaction').remove(); - } - - $.each(this.buttonsIds, function (key, value) { - $(value).removeClass('no-display'); - }); - - $(this.addSelectedButtonSelector).addClass('no-display'); - $(this.deleteButtonSelector) - .addClass('media-gallery-actions-buttons') - .removeClass('primary'); + $(this.buttons).removeClass('no-display'); + $(this.massactionButtonsSelector).remove(); }, /** * Activate mass action buttons view */ activateMassactionButtonView: function () { - this.originDeleteSelector = $(this.deleteButtonSelector).clone(); - $(this.originDeleteSelector).click(function () { - $(window).trigger('massAction.MediaGallery'); - }); - this.originCancelEvent = $('#cancel').clone(true, true); + var buttonsContainer; - $.each(this.buttonsIds, function (key, value) { - $(value).addClass('no-display'); - }); + $(this.buttons).addClass('no-display'); - $(this.deleteButtonSelector) - .removeClass('media-gallery-actions-buttons') - .text($t('Delete Selected')) - .addClass('primary'); - - if (!$(this.cancelMassactionButtonSelector).length) { - $(this.pageActionsSelector).append(this.cancelMassactionButton); - this.isCancelButtonInserted = true; - } else { - $(this.cancelMassactionButtonSelector).replaceWith(this.cancelMassactionButton); - } - $('#cancel_massaction').on('click', function () { - $(window).trigger('terminateMassAction.MediaGallery'); - }).applyBindings(); - - $(this.deleteButtonSelector).off('click').on('click', function () { - $(this.deleteButtonSelector).trigger('massDelete'); - }.bind(this)); + buttonsContainer = this.area === this.standAloneArea ? + this.buttonsSelectorStandalone : + this.buttonsSelectorSlidePanel; + $(buttonsContainer).append(massactionButtons); + $(this.massactionButtonsSelector).applyBindings(); }, /** @@ -133,7 +88,15 @@ define([ */ changePageTitle: function () { var title = $('h1:contains(' + this.standAloneTitle + ')'), - titleSelector = title.length === 1 ? title : $('h1:contains(' + this.slidePanelTitle + ')'); + titleSelector; + + if (title.length === 1) { + titleSelector = title; + this.area = this.standAloneArea; + } else { + titleSelector = $('h1:contains(' + this.slidePanelTitle + ')'); + this.area = this.slidepanelArea; + } if (this.massActionMode()) { this.defaultTitle = titleSelector.text(); diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js index 8114305a3b29c..4f09854005f23 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js @@ -16,6 +16,7 @@ define([ return Component.extend({ defaults: { + deleteButtonSelector: '#delete_selected_massaction', deleteImagesSelector: '#delete_massaction', mediaGalleryImageDetailsName: 'mediaGalleryImageDetails', modules: { @@ -86,6 +87,7 @@ define([ this.massActionMode(false); this.switchMode(); + this.imageModel().updateSelected(); }.bind(this)); }, @@ -124,7 +126,7 @@ define([ */ handleDeleteAction: function () { if (this.massActionMode()) { - $(this.massactionView().deleteButtonSelector).on('massDelete', function () { + $(this.deleteButtonSelector).on('massDelete.MediaGallery', function () { if (this.getSelectedCount() < 1) { uiAlert({ content: $t('You need to select at least one image') diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/image/image-details.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/image/image-details.js index d0d37d49329e0..db42f155501c3 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/image/image-details.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/image/image-details.js @@ -135,6 +135,16 @@ define([ return _.isArray(value); }, + /** + * Is value not empty + * + * @param {*} value + * @returns {Boolean} + */ + notEmpty: function (value) { + return value.length > 0; + }, + /** * Get name and number text for used in link * @@ -151,7 +161,7 @@ define([ * @param {String} link */ getFilterUrl: function (link) { - return link + '?filters[asset_id]=' + this.image().id; + return link + '?filters[asset_id]=[' + this.image().id + ']'; }, /** diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image.html b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image.html index 4a8350231a0fd..3b88c58201be7 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image.html +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image.html @@ -29,7 +29,7 @@