Skip to content

Commit ee5d56a

Browse files
🔃 [Magento Community Engineering] Community Contributions - 2.4-develop
Accepted Community Pull Requests: - #26246: #26245: Magento does not send an email about a refunded grouped product (by @atwixfirster) - #26279: Fix issue #26276 with clonning quote billing address street (by @yutv) - #25488: Update composer dependency to fix Redis Key Expiery (by @toxix) - #26502: {ASI} :- Error message to be cached for grid data storage component (by @konarshankar07) - #26781: Code hygeine in bundle option graphql resolver (by @moloughlin) - #26782: Module_Cms MFTF test improvements (by @ajithkumar-maragathavel) - #26715: Unit test for \Magento\Captcha\Observer\ResetAttemptForBackendObserver and ResetAttemptForFrontendObserver (by @karyna-tsymbal-atwix) - #26770: Unit tests for Magento\Csp\Model\Mode\ConfigManager and Magento\Csp\Observer\Render (by @karyna-tsymbal-atwix) - #25249: upgrade method delete by ids to inject array skus (by @sarron93) - #24843: Issue #24842: Unable to delete custom option file in admin order create (by @adrian-martinez-interactiv4) - #26142: Textarea patch 1 (by @textarea) - #26764: LoadCssAsync html format fixed for critical css (by @srsathish92) - #25246: Warning when Search Terms page is opened by clicking option at the footer (by @vishalverma279) - #26714: Deprecated redundant class (by @drpayyne) Fixed GitHub Issues: - #26245: Magento does not send an email about a refunded grouped product (reported by @atwixfirster) has been fixed in #26246 by @atwixfirster in 2.4-develop branch Related commits: 1. 7141278 2. a5f0d30 3. b1d99e5 4. dfc44a1 - #26276: Checkout. Quote Address Street cloning issue (reported by @yutv) has been fixed in #26279 by @yutv in 2.4-develop branch Related commits: 1. 2687221 2. 4e81fd8 3. 974f5e9 - #25487: Redis cache grows unilimmited (reported by @toxix) has been fixed in #25488 by @toxix in 2.4-develop branch Related commits: 1. 49809a7 2. d4931ec 3. 02edd9d 4. 3b89fc4 5. 85e71f5 6. a8f1ee7 7. 566c625 8. 6d0fa26 - #863: How to switch base,thumbnail images in magento 2 back end (reported by @nmohanswe) has been fixed in #26502 by @konarshankar07 in 2.4-develop branch Related commits: 1. c9d5e8e 2. 8357cf8 3. a17c2a5 4. 7fafb2d 5. 9de1e59 6. c3ba2ce 7. 0aef9ad 8. 7ef0c88 9. a1d2a84 10. 1f2ed90 - #24842: Unable to delete custom option file in admin order create (reported by @adrian-martinez-interactiv4) has been fixed in #24843 by @adrian-martinez-interactiv4 in 2.4-develop branch Related commits: 1. 504dd91 2. 3eb4110 3. 6e4a041 - #26141: Modal Popup and Custom subTitle erased (reported by @textarea) has been fixed in #26142 by @textarea in 2.4-develop branch Related commits: 1. 81b580a 2. 75b6674 3. 86023f9 4. a121250 5. fcce5b8 6. c4db941 7. 435c3fe - #26760: Validate html error when enable critical css (reported by @minhluan259) has been fixed in #26764 by @srsathish92 in 2.4-develop branch Related commits: 1. f9697f5 2. bf21c4b 3. 2881e48 4. 1e50859 - #25245: Warning when Search Terms page is opened by clicking option at the footer (reported by @vishalverma279) has been fixed in #25246 by @vishalverma279 in 2.4-develop branch Related commits: 1. 6b3bd36 2. c7483ba 3. 7b99e51 4. c58a7e3 5. 1e14f00 6. 1760043 7. d1f1eba - #14885: Refactoring: Code duplication EmailSender / ShipmentSender and so on (reported by @amenk) has been fixed in #26714 by @drpayyne in 2.4-develop branch Related commits: 1. 4e1bfde 2. 2f591a8 3. 5967ed4 4. 997a0de
2 parents 15269d9 + fd42cdd commit ee5d56a

File tree

39 files changed

+1734
-202
lines changed

39 files changed

+1734
-202
lines changed

app/code/Magento/BundleGraphQl/Model/Resolver/Options/Collection.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,7 @@ public function addParentFilterData(int $parentId, int $parentEntityId, string $
7878
public function getOptionsByParentId(int $parentId) : array
7979
{
8080
$options = $this->fetch();
81-
if (!isset($options[$parentId])) {
82-
return [];
83-
}
84-
85-
return $options[$parentId];
81+
return $options[$parentId] ?? [];
8682
}
8783

8884
/**
@@ -115,7 +111,7 @@ private function fetch() : array
115111

116112
$this->extensionAttributesJoinProcessor->process($optionsCollection);
117113
if (empty($optionsCollection->getData())) {
118-
return null;
114+
return [];
119115
}
120116

121117
/** @var \Magento\Bundle\Model\Option $option */
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Captcha\Test\Unit\Observer;
10+
11+
use Magento\Captcha\Model\ResourceModel\Log;
12+
use Magento\Captcha\Model\ResourceModel\LogFactory;
13+
use Magento\Captcha\Observer\ResetAttemptForBackendObserver;
14+
use Magento\Framework\Event;
15+
use Magento\Framework\Event\Observer;
16+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
17+
use PHPUnit\Framework\MockObject\MockObject;
18+
use PHPUnit\Framework\TestCase;
19+
20+
/**
21+
* Unit test for \Magento\Captcha\Observer\ResetAttemptForBackendObserver
22+
*/
23+
class ResetAttemptForBackendObserverTest extends TestCase
24+
{
25+
/**
26+
* Test that the method resets attempts for Backend
27+
*/
28+
public function testExecuteExpectsDeleteUserAttemptsCalled()
29+
{
30+
$logMock = $this->createMock(Log::class);
31+
$logMock->expects($this->once())->method('deleteUserAttempts')->willReturnSelf();
32+
33+
$resLogFactoryMock = $this->createMock(LogFactory::class);
34+
$resLogFactoryMock->expects($this->once())
35+
->method('create')
36+
->willReturn($logMock);
37+
38+
/** @var MockObject|Observer $eventObserverMock */
39+
$eventObserverMock = $this->createPartialMock(Observer::class, ['getUser']);
40+
$eventMock = $this->createMock(Event::class);
41+
$eventObserverMock->expects($this->once())
42+
->method('getUser')
43+
->willReturn($eventMock);
44+
45+
$objectManager = new ObjectManagerHelper($this);
46+
/** @var ResetAttemptForBackendObserver $observer */
47+
$observer = $objectManager->getObject(
48+
ResetAttemptForBackendObserver::class,
49+
['resLogFactory' => $resLogFactoryMock]
50+
);
51+
$this->assertInstanceOf(Log::class, $observer->execute($eventObserverMock));
52+
}
53+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Captcha\Test\Unit\Observer;
10+
11+
use Magento\Captcha\Model\ResourceModel\Log;
12+
use Magento\Captcha\Model\ResourceModel\LogFactory;
13+
use Magento\Captcha\Observer\ResetAttemptForFrontendObserver;
14+
use Magento\Customer\Model\Customer;
15+
use Magento\Framework\Event\Observer;
16+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
17+
use PHPUnit\Framework\MockObject\MockObject;
18+
use PHPUnit\Framework\TestCase;
19+
20+
/**
21+
* Unit test for \Magento\Captcha\Observer\ResetAttemptForFrontendObserver
22+
*/
23+
class ResetAttemptForFrontendObserverTest extends TestCase
24+
{
25+
/**
26+
* Test that the method resets attempts for Frontend
27+
*/
28+
public function testExecuteExpectsDeleteUserAttemptsCalled()
29+
{
30+
$logMock = $this->createMock(Log::class);
31+
$logMock->expects($this->once())->method('deleteUserAttempts')->willReturnSelf();
32+
33+
$resLogFactoryMock = $this->createMock(LogFactory::class);
34+
$resLogFactoryMock->expects($this->once())
35+
->method('create')
36+
->willReturn($logMock);
37+
38+
/** @var MockObject|Observer $eventObserverMock */
39+
$eventObserverMock = $this->createPartialMock(Observer::class, ['getModel']);
40+
$eventObserverMock->expects($this->once())
41+
->method('getModel')
42+
->willReturn($this->createMock(Customer::class));
43+
44+
$objectManager = new ObjectManagerHelper($this);
45+
/** @var ResetAttemptForFrontendObserver $observer */
46+
$observer = $objectManager->getObject(
47+
ResetAttemptForFrontendObserver::class,
48+
['resLogFactory' => $resLogFactoryMock]
49+
);
50+
$this->assertInstanceOf(Log::class, $observer->execute($eventObserverMock));
51+
}
52+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Catalog\Api;
8+
9+
/**
10+
* @api
11+
* @since 100.0.2
12+
*/
13+
interface CategoryListDeleteBySkuInterface
14+
{
15+
/**
16+
* Delete by skus list
17+
*
18+
* @param int $categoryId
19+
* @param string[] $productSkuList
20+
* @return bool
21+
*
22+
* @throws \Magento\Framework\Exception\CouldNotSaveException
23+
* @throws \Magento\Framework\Exception\NoSuchEntityException
24+
* @throws \Magento\Framework\Exception\InputException
25+
*/
26+
public function deleteBySkus(int $categoryId, array $productSkuList): bool;
27+
}

app/code/Magento/Catalog/Model/CategoryLinkRepository.php

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,52 @@
66

77
namespace Magento\Catalog\Model;
88

9-
use Magento\Framework\Exception\InputException;
9+
use Magento\Catalog\Api\CategoryLinkRepositoryInterface;
10+
use Magento\Catalog\Api\CategoryListDeleteBySkuInterface;
11+
use Magento\Catalog\Api\CategoryRepositoryInterface;
12+
use Magento\Catalog\Api\ProductRepositoryInterface;
13+
use Magento\Catalog\Model\ResourceModel\Product;
14+
use Magento\Framework\App\ObjectManager;
1015
use Magento\Framework\Exception\CouldNotSaveException;
16+
use Magento\Framework\Exception\InputException;
1117

12-
class CategoryLinkRepository implements \Magento\Catalog\Api\CategoryLinkRepositoryInterface
18+
/**
19+
* @inheritdoc
20+
*/
21+
class CategoryLinkRepository implements CategoryLinkRepositoryInterface, CategoryListDeleteBySkuInterface
1322
{
1423
/**
1524
* @var CategoryRepository
1625
*/
1726
protected $categoryRepository;
1827

1928
/**
20-
* @var \Magento\Catalog\Api\ProductRepositoryInterface
29+
* @var ProductRepositoryInterface
2130
*/
2231
protected $productRepository;
2332

2433
/**
25-
* @param \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository
26-
* @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
34+
* @var Product
35+
*/
36+
private $productResource;
37+
38+
/**
39+
* @param CategoryRepositoryInterface $categoryRepository
40+
* @param ProductRepositoryInterface $productRepository
41+
* @param Product $productResource
2742
*/
2843
public function __construct(
29-
\Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository,
30-
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository
44+
CategoryRepositoryInterface $categoryRepository,
45+
ProductRepositoryInterface $productRepository,
46+
Product $productResource = null
3147
) {
3248
$this->categoryRepository = $categoryRepository;
3349
$this->productRepository = $productRepository;
50+
$this->productResource = $productResource ?? ObjectManager::getInstance()->get(Product::class);
3451
}
3552

3653
/**
37-
* {@inheritdoc}
54+
* @inheritdoc
3855
*/
3956
public function save(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $productLink)
4057
{
@@ -60,15 +77,15 @@ public function save(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $pro
6077
}
6178

6279
/**
63-
* {@inheritdoc}
80+
* @inheritdoc
6481
*/
6582
public function delete(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $productLink)
6683
{
6784
return $this->deleteByIds($productLink->getCategoryId(), $productLink->getSku());
6885
}
6986

7087
/**
71-
* {@inheritdoc}
88+
* @inheritdoc
7289
*/
7390
public function deleteByIds($categoryId, $sku)
7491
{
@@ -101,4 +118,44 @@ public function deleteByIds($categoryId, $sku)
101118
}
102119
return true;
103120
}
121+
122+
/**
123+
* @inheritdoc
124+
*/
125+
public function deleteBySkus(int $categoryId, array $productSkuList): bool
126+
{
127+
$category = $this->categoryRepository->get($categoryId);
128+
$products = $this->productResource->getProductsIdsBySkus($productSkuList);
129+
130+
if (!$products) {
131+
throw new InputException(__("The category doesn't contain the specified products."));
132+
}
133+
134+
$productPositions = $category->getProductsPosition();
135+
136+
foreach ($products as $productId) {
137+
if (isset($productPositions[$productId])) {
138+
unset($productPositions[$productId]);
139+
}
140+
}
141+
142+
$category->setPostedProducts($productPositions);
143+
144+
try {
145+
$category->save();
146+
} catch (\Exception $e) {
147+
throw new CouldNotSaveException(
148+
__(
149+
'Could not save products "%products" to category %category',
150+
[
151+
"products" => implode(',', $productSkuList),
152+
"category" => $category->getId()
153+
]
154+
),
155+
$e
156+
);
157+
}
158+
159+
return true;
160+
}
104161
}

0 commit comments

Comments
 (0)