diff --git a/app/code/Magento/Downloadable/Controller/Download/Sample.php b/app/code/Magento/Downloadable/Controller/Download/Sample.php index e2561092a7592..839083b320878 100644 --- a/app/code/Magento/Downloadable/Controller/Download/Sample.php +++ b/app/code/Magento/Downloadable/Controller/Download/Sample.php @@ -3,14 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + declare(strict_types=1); namespace Magento\Downloadable\Controller\Download; +use Magento\Downloadable\Controller\Download; use Magento\Downloadable\Helper\Download as DownloadHelper; +use Magento\Downloadable\Helper\File; use Magento\Downloadable\Model\RelatedProductRetriever; use Magento\Downloadable\Model\Sample as SampleModel; +use Magento\Downloadable\Model\SampleFactory; use Magento\Framework\App\Action\Context; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResponseInterface; /** @@ -18,24 +25,49 @@ * * @SuppressWarnings(PHPMD.AllPurposeAction) */ -class Sample extends \Magento\Downloadable\Controller\Download +class Sample extends Download { /** * @var RelatedProductRetriever */ private $relatedProductRetriever; + /** + * @var File + */ + private $file; + + /** + * @var SampleFactory + */ + private $sampleFactory; + + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + /** * @param Context $context * @param RelatedProductRetriever $relatedProductRetriever + * @param File|null $file + * @param SampleFactory|null $sampleFactory + * @param StockConfigurationInterface|null $stockConfiguration */ public function __construct( Context $context, - RelatedProductRetriever $relatedProductRetriever + RelatedProductRetriever $relatedProductRetriever, + ?File $file = null, + ?SampleFactory $sampleFactory = null, + ?StockConfigurationInterface $stockConfiguration = null ) { parent::__construct($context); $this->relatedProductRetriever = $relatedProductRetriever; + $this->file = $file ?: ObjectManager::getInstance()->get(File::class); + $this->sampleFactory = $sampleFactory ?: ObjectManager::getInstance()->get(SampleFactory::class); + $this->stockConfiguration = $stockConfiguration + ?: ObjectManager::getInstance()->get(StockConfigurationInterface::class); } /** @@ -47,43 +79,60 @@ public function execute() { $sampleId = $this->getRequest()->getParam('sample_id', 0); /** @var SampleModel $sample */ - $sample = $this->_objectManager->create(SampleModel::class); + $sample = $this->sampleFactory->create(); $sample->load($sampleId); - if ($sample->getId() && $this->isProductSalable($sample)) { - $resource = ''; - $resourceType = ''; - if ($sample->getSampleType() == DownloadHelper::LINK_TYPE_URL) { - $resource = $sample->getSampleUrl(); - $resourceType = DownloadHelper::LINK_TYPE_URL; - } elseif ($sample->getSampleType() == DownloadHelper::LINK_TYPE_FILE) { - /** @var \Magento\Downloadable\Helper\File $helper */ - $helper = $this->_objectManager->get(\Magento\Downloadable\Helper\File::class); - $resource = $helper->getFilePath($sample->getBasePath(), $sample->getSampleFile()); - $resourceType = DownloadHelper::LINK_TYPE_FILE; - } - try { - $this->_processDownload($resource, $resourceType); - // phpcs:ignore Magento2.Security.LanguageConstruct.ExitUsage - exit(0); - } catch (\Exception $e) { - $this->messageManager->addError( - __('Sorry, there was an error getting requested content. Please contact the store owner.') - ); - } + if ($this->isCanDownload($sample)) { + $this->download($sample); } return $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl()); } /** - * Check is related product salable. + * Is sample can be downloaded * * @param SampleModel $sample * @return bool */ - private function isProductSalable(SampleModel $sample): bool + private function isCanDownload(SampleModel $sample): bool { $product = $this->relatedProductRetriever->getProduct((int) $sample->getProductId()); - return $product ? $product->isSalable() : false; + if ($product && $sample->getId()) { + $isProductEnabled = (int) $product->getStatus() === Status::STATUS_ENABLED; + + return $product->isSalable() || $this->stockConfiguration->isShowOutOfStock() && $isProductEnabled; + } + + return false; + } + + /** + * Download process + * + * @param SampleModel $sample + * @return void + */ + private function download(SampleModel $sample): void + { + $resource = ''; + $resourceType = ''; + + if ($sample->getSampleType() === DownloadHelper::LINK_TYPE_URL) { + $resource = $sample->getSampleUrl(); + $resourceType = DownloadHelper::LINK_TYPE_URL; + } elseif ($sample->getSampleType() === DownloadHelper::LINK_TYPE_FILE) { + $resource = $this->file->getFilePath($sample->getBasePath(), $sample->getSampleFile()); + $resourceType = DownloadHelper::LINK_TYPE_FILE; + } + + try { + $this->_processDownload($resource, $resourceType); + // phpcs:ignore Magento2.Security.LanguageConstruct.ExitUsage + exit(0); + } catch (\Exception $e) { + $this->messageManager->addErrorMessage( + __('Sorry, there was an error getting requested content. Please contact the store owner.') + ); + } } } diff --git a/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml index 2986532ef1138..9dca730dfd5c5 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml @@ -105,4 +105,15 @@ downloadableLink1 downloadableLink2 + + downloadableproduct + downloadable + 4 + DownloadableProduct + 99.99 + 50 + 1 + 0 + downloadableproduct + diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyOutOfStockDownloadableProductSamplesAreAccessibleTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyOutOfStockDownloadableProductSamplesAreAccessibleTest.xml new file mode 100644 index 0000000000000..337d4c7dd38b5 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyOutOfStockDownloadableProductSamplesAreAccessibleTest.xml @@ -0,0 +1,79 @@ + + + + + + + + + + <description value="Samples of Downloadable Products are accessible, if product is out of stock"/> + <severity value="MAJOR"/> + <testCaseId value="MC-35639"/> + <group value="downloadable"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Enable show out of stock product --> + <magentoCLI stepKey="enableShowOutOfStockProduct" command="config:set cataloginventory/options/show_out_of_stock 1"/> + + <!-- Add downloadable domains --> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> + + <!-- Create category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + + <!-- Create downloadable product --> + <createData entity="DownloadableProductWithoutLinksOutOfStock" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Add downloadable link --> + <createData entity="downloadableLink1" stepKey="addDownloadableLink"> + <requiredEntity createDataKey="createProduct"/> + </createData> + + <!-- Add downloadable sample --> + <createData entity="DownloadableSample" stepKey="addDownloadableSample"> + <requiredEntity createDataKey="createProduct"/> + </createData> + </before> + <after> + <!-- Disable show out of stock product --> + <magentoCLI stepKey="enableShowOutOfStockProduct" command="config:set cataloginventory/options/show_out_of_stock 0"/> + + <!-- Remove downloadable domains --> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> + + <!-- Delete product --> + <deleteData createDataKey="createProduct" stepKey="deleteDownloadableProduct"/> + + <!-- Delete category --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Admin logout --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Open Downloadable product from precondition on Storefront --> + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage"> + <argument name="productUrl" value="$createProduct.custom_attributes[url_key]$"/> + </actionGroup> + + <!-- Sample url is accessible --> + <actionGroup ref="AssertStorefrontSeeElementActionGroup" stepKey="seeDownloadableSample"> + <argument name="selector" value="{{StorefrontDownloadableProductSection.downloadableSampleLabel(DownloadableSample.title)}}"/> + </actionGroup> + <click selector="{{StorefrontDownloadableProductSection.downloadableSampleLabel(DownloadableSample.title)}}" stepKey="clickDownloadableSample"/> + <switchToNextTab stepKey="switchToSampleTab"/> + <wait time="2" stepKey="waitToMakeSureThereWillBeNoRedirectToHomePage"/> + <seeInCurrentUrl url="downloadable/download/sample/sample_id/" stepKey="amOnSampleDownloadPage"/> + <closeTab stepKey="closeSampleTab"/> + </test> +</tests>