Skip to content

Commit 21d65a6

Browse files
authored
Merge pull request magento#6943 from magento-l3/PR_20210609_1
L3 Bugfix delivery. 2.4-develop
2 parents f45fe09 + 6ae3cc9 commit 21d65a6

File tree

23 files changed

+846
-31
lines changed

23 files changed

+846
-31
lines changed

app/code/Magento/Backend/Block/Widget/Form.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ protected function _applyTypeSpecificConfig($inputType, $element, \Magento\Eav\M
230230
case 'date':
231231
$element->setDateFormat($this->_localeDate->getDateFormatWithLongYear());
232232
break;
233+
case 'datetime':
234+
$element->setDateFormat($this->_localeDate->getDateFormatWithLongYear());
235+
$element->setTimeFormat($this->_localeDate->getTimeFormat());
236+
break;
233237
case 'multiline':
234238
$element->setLineCount($attribute->getMultilineCount());
235239
break;
@@ -246,7 +250,13 @@ protected function _applyTypeSpecificConfig($inputType, $element, \Magento\Eav\M
246250
*/
247251
protected function _addElementTypes(\Magento\Framework\Data\Form\AbstractForm $baseElement)
248252
{
249-
$types = $this->_getAdditionalElementTypes();
253+
$types = array_merge(
254+
[
255+
'datetime' => 'date'
256+
],
257+
$this->_getAdditionalElementTypes()
258+
);
259+
250260
foreach ($types as $code => $className) {
251261
$baseElement->addType($code, $className);
252262
}

app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute;
88

99
use Magento\AsynchronousOperations\Api\Data\OperationInterface;
10+
use Magento\Catalog\Model\Product\Filter\DateTime as DateTimeFilter;
1011
use Magento\Catalog\Model\ProductFactory;
1112
use Magento\Catalog\Api\Data\ProductAttributeInterface;
1213
use Magento\Eav\Model\Config;
1314
use Magento\Framework\App\Action\HttpPostActionInterface;
1415
use Magento\Backend\App\Action;
1516
use Magento\Framework\App\ObjectManager;
1617
use Magento\Framework\Exception\LocalizedException;
18+
use Magento\Framework\Stdlib\DateTime;
1719
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
1820

1921
/**
@@ -67,6 +69,11 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut
6769
*/
6870
private $productFactory;
6971

72+
/**
73+
* @var DateTimeFilter
74+
*/
75+
private $dateTimeFilter;
76+
7077
/**
7178
* @param Action\Context $context
7279
* @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper
@@ -76,9 +83,10 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut
7683
* @param \Magento\Framework\Serialize\SerializerInterface $serializer
7784
* @param \Magento\Authorization\Model\UserContextInterface $userContext
7885
* @param int $bulkSize
79-
* @param TimezoneInterface $timezone
80-
* @param Config $eavConfig
81-
* @param ProductFactory $productFactory
86+
* @param TimezoneInterface|null $timezone
87+
* @param Config|null $eavConfig
88+
* @param ProductFactory|null $productFactory
89+
* @param DateTimeFilter|null $dateTimeFilter
8290
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
8391
*/
8492
public function __construct(
@@ -92,7 +100,8 @@ public function __construct(
92100
int $bulkSize = 100,
93101
TimezoneInterface $timezone = null,
94102
Config $eavConfig = null,
95-
ProductFactory $productFactory = null
103+
ProductFactory $productFactory = null,
104+
?DateTimeFilter $dateTimeFilter = null
96105
) {
97106
parent::__construct($context, $attributeHelper);
98107
$this->bulkManagement = $bulkManagement;
@@ -106,6 +115,7 @@ public function __construct(
106115
$this->eavConfig = $eavConfig ?: ObjectManager::getInstance()
107116
->get(Config::class);
108117
$this->productFactory = $productFactory ?? ObjectManager::getInstance()->get(ProductFactory::class);
118+
$this->dateTimeFilter = $dateTimeFilter ?? ObjectManager::getInstance()->get(DateTimeFilter::class);
109119
}
110120

111121
/**
@@ -155,8 +165,6 @@ public function execute()
155165
*/
156166
private function sanitizeProductAttributes($attributesData)
157167
{
158-
$dateFormat = $this->timezone->getDateFormat(\IntlDateFormatter::SHORT);
159-
160168
foreach ($attributesData as $attributeCode => $value) {
161169
if ($attributeCode === ProductAttributeInterface::CODE_HAS_WEIGHT) {
162170
continue;
@@ -170,16 +178,10 @@ private function sanitizeProductAttributes($attributesData)
170178
}
171179

172180
if ($attribute->getBackendType() === 'datetime') {
173-
if (!empty($value)) {
174-
$filterInput = new \Zend_Filter_LocalizedToNormalized(['date_format' => $dateFormat]);
175-
$filterInternal = new \Zend_Filter_NormalizedToLocalized(
176-
['date_format' => \Magento\Framework\Stdlib\DateTime::DATE_INTERNAL_FORMAT]
177-
);
178-
$value = $filterInternal->filter($filterInput->filter($value));
179-
} else {
180-
$value = null;
181-
}
182-
$attributesData[$attributeCode] = $value;
181+
$attributesData[$attributeCode] = $this->filterDate(
182+
$value,
183+
$attribute->getFrontendInput() === 'datetime'
184+
);
183185
} elseif ($attribute->getFrontendInput() === 'multiselect') {
184186
// Check if 'Change' checkbox has been checked by admin for this attribute
185187
$isChanged = (bool)$this->getRequest()->getPost('toggle_' . $attributeCode);
@@ -196,6 +198,24 @@ private function sanitizeProductAttributes($attributesData)
196198
return $attributesData;
197199
}
198200

201+
/**
202+
* Get the date and time value in internal format and timezone
203+
*
204+
* @param string $value
205+
* @param bool $isDatetime
206+
* @return string|null
207+
* @throws LocalizedException
208+
*/
209+
private function filterDate(string $value, bool $isDatetime = false): ?string
210+
{
211+
$date = !empty($value) ? $this->dateTimeFilter->filter($value) : null;
212+
if ($date && $isDatetime) {
213+
$date = $this->timezone->convertConfigTimeToUtc($date, DateTime::DATETIME_PHP_FORMAT);
214+
}
215+
216+
return $date;
217+
}
218+
199219
/**
200220
* Validate product attributes data.
201221
*

app/code/Magento/Catalog/Model/Attribute/Backend/Startdate.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
/**
99
*
10-
* Speical Start Date attribute backend
10+
* Special Start Date attribute backend
1111
*
1212
* @api
1313
*
@@ -83,7 +83,7 @@ public function validate($object)
8383
$attr = $this->getAttribute();
8484
$maxDate = $attr->getMaxValue();
8585
$startDate = $this->_getValueForSave($object);
86-
if ($startDate === false) {
86+
if ($startDate === false || $startDate === null) {
8787
return true;
8888
}
8989

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Product\Gallery;
9+
10+
use Magento\Catalog\Api\Data\ProductAttributeInterface;
11+
use Magento\Catalog\Api\Data\ProductInterface;
12+
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
13+
use Magento\Catalog\Model\Product;
14+
use Magento\Catalog\Model\ResourceModel\Product\Gallery;
15+
use Magento\Eav\Model\ResourceModel\AttributeValue;
16+
use Magento\Framework\EntityManager\EntityMetadata;
17+
use Magento\Framework\EntityManager\MetadataPool;
18+
use Magento\Framework\EntityManager\Operation\ExtensionInterface;
19+
use Magento\Framework\Serialize\Serializer\Json;
20+
21+
/**
22+
* Copy gallery data from one product to another
23+
*/
24+
class CopyHandler implements ExtensionInterface
25+
{
26+
/**
27+
* @var EntityMetadata
28+
*/
29+
private $metadata;
30+
31+
/**
32+
* @var Gallery
33+
*/
34+
private $galleryResourceModel;
35+
36+
/**
37+
* @var ProductAttributeRepositoryInterface
38+
*/
39+
private $attributeRepository;
40+
41+
/**
42+
* @var AttributeValue
43+
*/
44+
private $attributeValue;
45+
46+
/**
47+
* @var Json
48+
*/
49+
private $json;
50+
51+
/**
52+
* @var ProductAttributeInterface
53+
*/
54+
private $attribute;
55+
56+
/**
57+
* @param MetadataPool $metadataPool
58+
* @param Gallery $galleryResourceModel
59+
* @param ProductAttributeRepositoryInterface $attributeRepository
60+
* @param AttributeValue $attributeValue
61+
* @param Json $json
62+
*/
63+
public function __construct(
64+
MetadataPool $metadataPool,
65+
Gallery $galleryResourceModel,
66+
ProductAttributeRepositoryInterface $attributeRepository,
67+
AttributeValue $attributeValue,
68+
Json $json
69+
) {
70+
$this->metadata = $metadataPool->getMetadata(ProductInterface::class);
71+
$this->galleryResourceModel = $galleryResourceModel;
72+
$this->attributeRepository = $attributeRepository;
73+
$this->attributeValue = $attributeValue;
74+
$this->json = $json;
75+
}
76+
77+
/**
78+
* Copy gallery data from one product to another
79+
*
80+
* @param Product $product
81+
* @param array $arguments
82+
* @return void
83+
*/
84+
public function execute($product, $arguments = []): void
85+
{
86+
$fromId = (int) $arguments['original_link_id'];
87+
$toId = $product->getData($this->metadata->getLinkField());
88+
$attributeId = $this->getAttribute()->getAttributeId();
89+
$valueIdMap = $this->galleryResourceModel->duplicate($attributeId, [], $fromId, $toId);
90+
$gallery = $this->getMediaGalleryCollection($product);
91+
92+
if (!empty($gallery['images'])) {
93+
$images = [];
94+
foreach ($gallery['images'] as $key => $image) {
95+
$valueId = $image['value_id'] ?? null;
96+
$newKey = $key;
97+
if ($valueId !== null) {
98+
$newValueId = $valueId;
99+
if (isset($valueIdMap[$valueId])) {
100+
$newValueId = $valueIdMap[$valueId];
101+
}
102+
if (((int) $valueId) === $key) {
103+
$newKey = $newValueId;
104+
}
105+
$image['value_id'] = $newValueId;
106+
}
107+
$images[$newKey] = $image;
108+
}
109+
$gallery['images'] = $images;
110+
$attrCode = $this->getAttribute()->getAttributeCode();
111+
$product->setData($attrCode, $gallery);
112+
}
113+
114+
//Copy media attribute values from one product to another
115+
if (isset($arguments['media_attribute_codes'])) {
116+
$values = $this->attributeValue->getValues(
117+
ProductInterface::class,
118+
$fromId,
119+
$arguments['media_attribute_codes']
120+
);
121+
if ($values) {
122+
foreach (array_keys($values) as $key) {
123+
$values[$key][$this->metadata->getLinkField()] = $product->getData($this->metadata->getLinkField());
124+
unset($values[$key]['value_id']);
125+
}
126+
$this->attributeValue->insertValues(
127+
ProductInterface::class,
128+
$values
129+
);
130+
}
131+
}
132+
}
133+
134+
/**
135+
* Get product media gallery collection
136+
*
137+
* @param Product $product
138+
* @return array
139+
*/
140+
private function getMediaGalleryCollection(Product $product): array
141+
{
142+
$attrCode = $this->getAttribute()->getAttributeCode();
143+
$value = $product->getData($attrCode);
144+
145+
if (is_array($value) && isset($value['images'])) {
146+
if (!is_array($value['images']) && strlen($value['images']) > 0) {
147+
$value['images'] = $this->json->unserialize($value['images']);
148+
}
149+
150+
if (!is_array($value['images'])) {
151+
$value['images'] = [];
152+
}
153+
}
154+
155+
return $value;
156+
}
157+
158+
/**
159+
* Returns media gallery attribute instance
160+
*
161+
* @return ProductAttributeInterface
162+
*/
163+
private function getAttribute(): ProductAttributeInterface
164+
{
165+
if (!$this->attribute) {
166+
$this->attribute = $this->attributeRepository->get(
167+
ProductInterface::MEDIA_GALLERY
168+
);
169+
}
170+
171+
return $this->attribute;
172+
}
173+
}

0 commit comments

Comments
 (0)