diff --git a/app/code/Magento/Backend/Block/GlobalSearch.php b/app/code/Magento/Backend/Block/GlobalSearch.php index f4a46283808f4..b00dce505ef9e 100644 --- a/app/code/Magento/Backend/Block/GlobalSearch.php +++ b/app/code/Magento/Backend/Block/GlobalSearch.php @@ -3,19 +3,63 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Block; +use Magento\Backend\Model\GlobalSearch\SearchEntityFactory; +use Magento\Backend\Model\GlobalSearch\SearchEntity; +use Magento\Framework\App\ObjectManager; + /** * @api * @since 100.0.2 */ class GlobalSearch extends \Magento\Backend\Block\Template { + const ENTITY_TYPE_PRODUCTS = 'Products'; + const ENTITY_TYPE_ORDERS = 'Orders'; + const ENTITY_TYPE_CUSTOMERS = 'Customers'; + const ENTITY_TYPE_PAGES = 'Pages'; + + /** + * Affiliation between entity types for global search and corresponding admin resources. + * + * @var array + */ + private $entityTypes = [ + self::ENTITY_TYPE_PRODUCTS => \Magento\Catalog\Controller\Adminhtml\Product::ADMIN_RESOURCE, + self::ENTITY_TYPE_ORDERS => \Magento\Sales\Controller\Adminhtml\Order::ADMIN_RESOURCE, + self::ENTITY_TYPE_CUSTOMERS => \Magento\Customer\Controller\Adminhtml\Index::ADMIN_RESOURCE, + self::ENTITY_TYPE_PAGES => \Magento\Cms\Controller\Adminhtml\Page\Index::ADMIN_RESOURCE, + ]; + + /** + * @var SearchEntityFactory + */ + private $searchEntityFactory; + /** * @var string */ protected $_template = 'Magento_Backend::system/search.phtml'; + /** + * @param Template\Context $context + * @param array $data + * @param SearchEntityFactory|null $searchEntityFactory + */ + public function __construct( + Template\Context $context, + array $data = [], + SearchEntityFactory $searchEntityFactory = null + ) { + $this->searchEntityFactory = $searchEntityFactory ?: ObjectManager::getInstance()->get( + SearchEntityFactory::class + ); + + parent::__construct($context, $data); + } + /** * Get components configuration * @return array @@ -34,4 +78,63 @@ public function getWidgetInitOptions() ] ]; } + + /** + * Get entities which are allowed to show. + * + * @return SearchEntity[] + */ + public function getEntitiesToShow() + { + $allowedEntityTypes = []; + $entitiesToShow = []; + + foreach ($this->entityTypes as $entityType => $resource) { + if ($this->getAuthorization()->isAllowed($resource)) { + $allowedEntityTypes[] = $entityType; + } + } + + foreach ($allowedEntityTypes as $entityType) { + $url = $this->getUrlEntityType($entityType); + + $searchEntity = $this->searchEntityFactory->create(); + $searchEntity->setId('searchPreview' . $entityType); + $searchEntity->setTitle('in ' . $entityType); + $searchEntity->setUrl($url); + + $entitiesToShow[] = $searchEntity; + } + + return $entitiesToShow; + } + + /** + * Get url path by entity type. + * + * @param string $entityType + * + * @return string + */ + private function getUrlEntityType(string $entityType) + { + $urlPath = ''; + + switch ($entityType) { + case self::ENTITY_TYPE_PRODUCTS: + $urlPath = 'catalog/product/index/'; + break; + case self::ENTITY_TYPE_ORDERS: + $urlPath = 'sales/order/index/'; + break; + case self::ENTITY_TYPE_CUSTOMERS: + $urlPath = 'customer/index/index/'; + break; + case self::ENTITY_TYPE_PAGES: + $urlPath = 'cms/page/index/'; + break; + } + + return $this->getUrl($urlPath); + } } diff --git a/app/code/Magento/Backend/Model/GlobalSearch/SearchEntity.php b/app/code/Magento/Backend/Model/GlobalSearch/SearchEntity.php new file mode 100644 index 0000000000000..18691802b9218 --- /dev/null +++ b/app/code/Magento/Backend/Model/GlobalSearch/SearchEntity.php @@ -0,0 +1,73 @@ +getData('id'); + } + + /** + * Get url. + * + * @return string + */ + public function getUrl() + { + return $this->getData('url'); + } + + /** + * Get title. + * + * @return string + */ + public function getTitle() + { + return $this->getData('title'); + } + + /** + * Set Id. + * + * @param string $value + */ + public function setId(string $value) + { + $this->setData('id', $value); + } + + /** + * Set url. + * + * @param string $value + */ + public function setUrl(string $value) + { + $this->setData('url', $value); + } + + /** + * Set title. + * + * @param string $value + */ + public function setTitle(string $value) + { + $this->setData('title', $value); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Block/GlobalSearchTest.php b/app/code/Magento/Backend/Test/Unit/Block/GlobalSearchTest.php new file mode 100644 index 0000000000000..6e6a9fe8120e7 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Block/GlobalSearchTest.php @@ -0,0 +1,101 @@ +authorization = $this->createMock(\Magento\Framework\AuthorizationInterface::class); + $this->urlBuilder = $this->createMock(\Magento\Framework\UrlInterface::class); + $context = $this->createMock(\Magento\Backend\Block\Template\Context::class); + + $context->expects($this->atLeastOnce())->method('getAuthorization')->willReturn($this->authorization); + $context->expects($this->atLeastOnce())->method('getUrlBuilder')->willReturn($this->urlBuilder); + + $this->searchEntityFactory = $this->createMock(\Magento\Backend\Model\GlobalSearch\SearchEntityFactory::class); + + $this->globalSearch = $objectManager->getObject( + GlobalSearch::class, + [ + 'context' => $context, + 'searchEntityFactory' => $this->searchEntityFactory, + ] + ); + } + + /** + * @param array $results + * @param int $expectedEntitiesQty + * + * @dataProvider getEntitiesToShowDataProvider + */ + public function testGetEntitiesToShow(array $results, int $expectedEntitiesQty) + { + $searchEntity = $this->createMock(SearchEntity::class); + + $this->authorization->expects($this->exactly(count($results)))->method('isAllowed') + ->willReturnOnConsecutiveCalls($results[0], $results[1], $results[2], $results[3]); + $this->urlBuilder->expects($this->exactly($expectedEntitiesQty)) + ->method('getUrl')->willReturn('some/url/is/here'); + $this->searchEntityFactory->expects($this->exactly($expectedEntitiesQty)) + ->method('create')->willReturn($searchEntity); + + $searchEntity->expects($this->exactly($expectedEntitiesQty))->method('setId'); + $searchEntity->expects($this->exactly($expectedEntitiesQty))->method('setTitle'); + $searchEntity->expects($this->exactly($expectedEntitiesQty))->method('setUrl'); + + $this->assertSame($expectedEntitiesQty, count($this->globalSearch->getEntitiesToShow())); + } + + public function getEntitiesToShowDataProvider() + { + return [ + [ + [true, false, true, false], + 2, + ], + [ + [true, true, true, true], + 4, + ], + [ + [false, false, false, false], + 0, + ], + ]; + } +} diff --git a/app/code/Magento/Backend/composer.json b/app/code/Magento/Backend/composer.json index d2cc8f76393f9..a25ebf155e3c2 100644 --- a/app/code/Magento/Backend/composer.json +++ b/app/code/Magento/Backend/composer.json @@ -9,6 +9,7 @@ "magento/module-eav": "101.0.*", "magento/module-reports": "100.2.*", "magento/module-sales": "101.0.*", + "magento/module-cms": "102.0.*", "magento/module-quote": "101.0.*", "magento/module-catalog": "102.0.*", "magento/module-user": "101.0.*", diff --git a/app/code/Magento/Backend/i18n/en_US.csv b/app/code/Magento/Backend/i18n/en_US.csv index f9f44f547e25b..aa28a670b9205 100644 --- a/app/code/Magento/Backend/i18n/en_US.csv +++ b/app/code/Magento/Backend/i18n/en_US.csv @@ -461,3 +461,7 @@ Pagination,Pagination "Alternative text for the next pages link in the pagination menu. If empty, default arrow image is used.","Alternative text for the next pages link in the pagination menu. If empty, default arrow image is used." "Anchor Text for Next","Anchor Text for Next" "Theme Name","Theme Name" +"In Products","In Products" +"In Orders","In Orders" +"In Customers","In Customers" +"In Pages","In Pages" diff --git a/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml b/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml index b50183ced29b4..3c65c0358eb57 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/system/search.phtml @@ -27,18 +27,15 @@