Skip to content

Commit d003fd2

Browse files
authored
Merge branch 'magento:2.4-develop' into accesibility-improvements
2 parents 2ef6ad7 + d01ee51 commit d003fd2

File tree

111 files changed

+8425
-391
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+8425
-391
lines changed

app/code/Magento/Backend/view/adminhtml/layout/default.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<body>
1616
<attribute name="id" value="html-body"/>
1717
<block name="require.js" class="Magento\Backend\Block\Page\RequireJs" template="Magento_Backend::page/js/require_js.phtml"/>
18+
<block class="Magento\Framework\View\Element\Template" name="head.critical" as="head.critical" template="Magento_Backend::page/container.phtml"/>
1819
<block class="Magento\Framework\View\Element\Template" name="head.additional" template="Magento_Backend::page/container.phtml"/>
1920
<referenceContainer name="global.notices">
2021
<block class="Magento\Backend\Block\Page\Notices" name="global_notices" as="global_notices" template="Magento_Backend::page/notices.phtml"/>
Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,45 @@
11
<?php
2-
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
2+
/************************************************************************
3+
*
4+
* Copyright 2024 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ************************************************************************
516
*/
617
declare(strict_types=1);
718

819
namespace Magento\BundleGraphQl\Model\Resolver;
920

21+
use Magento\Bundle\Model\Product\Price;
1022
use Magento\Catalog\Model\Product;
1123
use Magento\Framework\Exception\LocalizedException;
24+
use Magento\Framework\Exception\NoSuchEntityException;
1225
use Magento\Framework\GraphQl\Config\Element\Field;
1326
use Magento\Framework\GraphQl\Query\ResolverInterface;
1427
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
28+
use Magento\Quote\Api\CartRepositoryInterface;
29+
use Magento\Quote\Api\Data\CartItemInterface;
1530

1631
class BundlePriceDetails implements ResolverInterface
1732
{
33+
/**
34+
* BundlePriceDetails Constructor
35+
*
36+
* @param CartRepositoryInterface $cartRepository
37+
*/
38+
public function __construct(
39+
private readonly CartRepositoryInterface $cartRepository
40+
) {
41+
}
42+
1843
/**
1944
* @inheritdoc
2045
*/
@@ -25,14 +50,44 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
2550
}
2651
/** @var Product $product */
2752
$product = $value['model'];
28-
2953
$price = $product->getPrice();
3054
$finalPrice = $product->getFinalPrice();
3155
$discountPercentage = ($price) ? (100 - (($finalPrice * 100) / $price)) : 0;
56+
if ((int)$product->getPriceType() === Price::PRICE_TYPE_DYNAMIC && isset($value['cart_item'])) {
57+
$discountPercentage = $this->getDiscountPercentageForBundleProduct($value['cart_item']);
58+
}
3259
return [
3360
'main_price' => $price,
3461
'main_final_price' => $finalPrice,
3562
'discount_percentage' => $discountPercentage
3663
];
3764
}
65+
66+
/**
67+
* Calculate discount percentage for bundle product with dynamic pricing enabled
68+
*
69+
* @param CartItemInterface $cartItem
70+
* @return float
71+
* @throws NoSuchEntityException
72+
*/
73+
private function getDiscountPercentageForBundleProduct(CartItemInterface $cartItem): float
74+
{
75+
if (empty($cartItem->getAppliedRuleIds())) {
76+
return 0;
77+
}
78+
$itemAmount = 0;
79+
$discountAmount = 0;
80+
$cart = $this->cartRepository->get($cartItem->getQuoteId());
81+
foreach ($cart->getAllItems() as $item) {
82+
if ($item->getParentItemId() == $cartItem->getId()) {
83+
$itemAmount += $item->getPrice();
84+
$discountAmount += $item->getDiscountAmount();
85+
}
86+
}
87+
if ($itemAmount && $discountAmount) {
88+
return ($discountAmount / $itemAmount) * 100;
89+
}
90+
91+
return 0;
92+
}
3893
}

app/code/Magento/Catalog/Block/Rss/Product/NewProducts.php

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
*/
66
namespace Magento\Catalog\Block\Rss\Product;
77

8-
use Magento\Framework\App\Rss\DataProviderInterface;
8+
use Magento\Framework\App\Rss\DataProviderInterface as DProviderInterface;
9+
use Magento\Framework\DataObject\IdentityInterface as IdInterface;
910

10-
/**
11-
* Class NewProducts
12-
* @package Magento\Catalog\Block\Rss\Product
13-
*/
14-
class NewProducts extends \Magento\Framework\View\Element\AbstractBlock implements DataProviderInterface
11+
class NewProducts extends \Magento\Framework\View\Element\AbstractBlock implements DProviderInterface, IdInterface
1512
{
13+
public const CACHE_TAG = 'rss_p_new';
14+
1615
/**
1716
* @var \Magento\Catalog\Helper\Image
1817
*/
@@ -55,6 +54,8 @@ public function __construct(
5554
}
5655

5756
/**
57+
* Configure class
58+
*
5859
* @return void
5960
*/
6061
protected function _construct()
@@ -64,15 +65,15 @@ protected function _construct()
6465
}
6566

6667
/**
67-
* {@inheritdoc}
68+
* @inheritdoc
6869
*/
6970
public function isAllowed()
7071
{
7172
return $this->_scopeConfig->isSetFlag('rss/catalog/new', \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
7273
}
7374

7475
/**
75-
* {@inheritdoc}
76+
* @inheritdoc
7677
*/
7778
public function getRssData()
7879
{
@@ -132,6 +133,8 @@ public function getRssData()
132133
}
133134

134135
/**
136+
* Get current store id
137+
*
135138
* @return int
136139
*/
137140
protected function getStoreId()
@@ -177,14 +180,16 @@ protected function renderPriceHtml(\Magento\Catalog\Model\Product $product)
177180
}
178181

179182
/**
180-
* {@inheritdoc}
183+
* @inheritdoc
181184
*/
182185
public function getCacheLifetime()
183186
{
184187
return 600;
185188
}
186189

187190
/**
191+
* Generate rss feed
192+
*
188193
* @return array
189194
*/
190195
public function getFeeds()
@@ -199,10 +204,18 @@ public function getFeeds()
199204
}
200205

201206
/**
202-
* {@inheritdoc}
207+
* @inheritdoc
203208
*/
204209
public function isAuthRequired()
205210
{
206211
return false;
207212
}
213+
214+
/**
215+
* @inheritdoc
216+
*/
217+
public function getIdentities()
218+
{
219+
return [self::CACHE_TAG];
220+
}
208221
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
/************************************************************************
3+
*
4+
* Copyright 2024 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ************************************************************************
16+
*/
17+
declare(strict_types=1);
18+
19+
namespace Magento\Catalog\Model\Category;
20+
21+
use Magento\Catalog\Model\Category;
22+
use Magento\Framework\App\Cache\Tag\StrategyInterface;
23+
use Magento\Framework\Model\AbstractModel;
24+
use Magento\Widget\Model\Widget\Instance;
25+
26+
/**
27+
* Get additional layout cache tag for category layout.
28+
*/
29+
class LayoutCacheTagResolver implements StrategyInterface
30+
{
31+
/**
32+
* @inheritDoc
33+
*/
34+
public function getTags($object)
35+
{
36+
if ($this->isExistingCategoryLayoutChange($object)) {
37+
return [
38+
str_replace('{{ID}}', (string) $object->getId(), Instance::SINGLE_CATEGORY_LAYOUT_HANDLE)
39+
];
40+
}
41+
return [];
42+
}
43+
44+
/**
45+
* Check if existing category page layout change
46+
*
47+
* @param Category $object
48+
* @return bool
49+
*/
50+
private function isExistingCategoryLayoutChange(Category $object): bool
51+
{
52+
return !$object->isObjectNew() && $this->isObjectChanged($object);
53+
}
54+
55+
/**
56+
* Check if the page layout of the given category is changed
57+
*
58+
* @param AbstractModel $object
59+
* @return bool
60+
*/
61+
private function isObjectChanged(AbstractModel $object): bool
62+
{
63+
$isChanged = false;
64+
$objectNewPageLayout = $object->getData('page_layout');
65+
$objectOldPageLayout = $object->getOrigData('page_layout');
66+
if ($objectNewPageLayout !== 'empty' &&
67+
$objectNewPageLayout !== $objectOldPageLayout
68+
) {
69+
$isChanged = true;
70+
}
71+
return $isChanged;
72+
}
73+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,6 +2413,7 @@ public function getIdentities()
24132413
|| $this->isObjectNew();
24142414
if ($isProductNew && ($isStatusChanged || $this->getStatus() == Status::STATUS_ENABLED)) {
24152415
$identities[] = \Magento\Catalog\Block\Product\NewProduct::CACHE_TAG;
2416+
$identities[] = \Magento\Catalog\Block\Rss\Product\NewProducts::CACHE_TAG;
24162417
}
24172418

24182419
return array_unique($identities);
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
/************************************************************************
3+
*
4+
* Copyright 2024 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ************************************************************************
16+
*/
17+
declare(strict_types=1);
18+
19+
namespace Magento\Catalog\Model\Product;
20+
21+
use Magento\Catalog\Model\Product;
22+
use Magento\Framework\App\Cache\Tag\StrategyInterface;
23+
use Magento\Framework\Model\AbstractModel;
24+
use Magento\Widget\Model\Widget\Instance;
25+
26+
/**
27+
* Get additional layout cache tag for product layout.
28+
*/
29+
class LayoutCacheTagResolver implements StrategyInterface
30+
{
31+
/**
32+
* @inheritDoc
33+
*/
34+
public function getTags($object)
35+
{
36+
if ($this->isExistingProductLayoutChange($object)) {
37+
return [
38+
str_replace('{{ID}}', (string) $object->getId(), Instance::SINGLE_PRODUCT_LAYOUT_HANDLE)
39+
];
40+
}
41+
return [];
42+
}
43+
44+
/**
45+
* Check if existing Product page layout change
46+
*
47+
* @param Product $object
48+
* @return bool
49+
*/
50+
private function isExistingProductLayoutChange(Product $object): bool
51+
{
52+
return !$object->isObjectNew() && $this->isObjectChanged($object);
53+
}
54+
55+
/**
56+
* Check if the page layout of the given product is changed
57+
*
58+
* @param AbstractModel $object
59+
* @return bool
60+
*/
61+
private function isObjectChanged(AbstractModel $object): bool
62+
{
63+
$isChanged = false;
64+
$objectNewPageLayout = $object->getData('page_layout');
65+
$objectOldPageLayout = $object->getOrigData('page_layout');
66+
if ($objectNewPageLayout !== 'empty' &&
67+
$objectNewPageLayout !== $objectOldPageLayout
68+
) {
69+
$isChanged = true;
70+
}
71+
return $isChanged;
72+
}
73+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,13 @@ public function save(ProductInterface $product, $saveOptions = false)
526526
$assignToCategories = false;
527527
$tierPrices = $product->getData('tier_price');
528528
$productDataToChange = $product->getData();
529+
530+
if (!$product->getSku()) {
531+
throw new CouldNotSaveException(
532+
__("The \"%1\" attribute value is empty. Set the attribute and try again.", "sku")
533+
);
534+
}
535+
529536
try {
530537
$existingProduct = $product->getId() ?
531538
$this->getById($product->getId()) :

app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,4 +1568,18 @@ public function testSaveExistingWithMediaGalleryEntries(): void
15681568
$this->model->save($this->product);
15691569
$this->assertEquals($expectedResult, $this->initializedProduct->getMediaGallery('images'));
15701570
}
1571+
1572+
/**
1573+
* @return void
1574+
*/
1575+
public function testSaveCouldNotSaveException(): void
1576+
{
1577+
$this->expectException(\Magento\Framework\Exception\CouldNotSaveException::class);
1578+
$productData = [
1579+
'name' => 'Simple Product',
1580+
'price' => 100
1581+
];
1582+
$this->product->setData($productData);
1583+
$this->model->save($this->product);
1584+
}
15711585
}

app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ private function getNoStockStatusChangesData(MockObject $extensionAttributesMock
983983
private function getNewProductProviderData(): array
984984
{
985985
return [
986-
['cat_p_1', 'cat_c_p_1', 'cat_p_new'],
986+
['cat_p_1', 'cat_c_p_1', 'cat_p_new', 'rss_p_new'],
987987
null,
988988
[
989989
'id' => 1,

0 commit comments

Comments
 (0)