diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index c2e9fa9cef24..ba46c6e72cf5 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ -# Magento Code of Conduct +# Mage-OS Code of Conduct ## Our Pledge diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d3fd535a6d57..500066abf97e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,13 +1,13 @@ # Contributing to Magento 2 code -Contributions to the Magento 2 codebase are done using the fork & pull model. -This contribution model has contributors maintaining their own fork of the Magento 2 repository. +Contributions to the Mage-OS Magento 2 codebase are done using the fork & pull model. +This contribution model has contributors maintaining their own fork of the Mage-OS Magento 2 repository. The forked repository is then used to submit a request to the base repository to "pull" a set of changes. For more information on pull requests please refer to [GitHub Help](https://help.github.com/articles/about-pull-requests/). Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes or optimizations. -The Magento 2 development team or community maintainers will review all issues and contributions submitted by the community of developers in the first in, first out order. +The Mage-OS Magento 2 development team or community maintainers will review all issues and contributions submitted by the community of developers in the first in, first out order. During the review we might require clarifications from the contributor. If there is no response from the contributor within two weeks, the pull request will be closed. @@ -17,24 +17,24 @@ For more detailed information on contribution please read our [beginners guide]( 1. Contributions must adhere to the [Magento coding standards](https://developer.adobe.com/commerce/php/coding-standards/). 2. Pull requests (PRs) must be accompanied by a meaningful description of their purpose. Comprehensive descriptions increase the chances of a pull request being merged quickly and without additional clarification requests. -3. Commits must be accompanied by meaningful commit messages. Please see the [Magento Pull Request Template](https://github.com/magento/magento2/blob/HEAD/.github/PULL_REQUEST_TEMPLATE.md) for more information. +3. Commits must be accompanied by meaningful commit messages. Please see the [Mage-OS Magento Pull Request Template](https://github.com/mage-os/mageos-magento2/blob/HEAD/.github/PULL_REQUEST_TEMPLATE.md) for more information. 4. PRs which include bug fixes must be accompanied with a step-by-step description of how to reproduce the bug. 5. PRs which include new logic or new features must be submitted along with: * Unit/integration test coverage * Proposed [documentation](https://developer.adobe.com/commerce) updates. Use feedback buttons __Edit in GitHub__ and __Log an issue__ at the top of a relevant topic. -6. For larger features or changes, please [open an issue](https://github.com/magento/magento2/issues) to discuss the proposed changes prior to development. This may prevent duplicate or unnecessary effort and allow other contributors to provide input. +6. For larger features or changes, please [open an issue](https://github.com/mage-os/mageos-magento2/issues) to discuss the proposed changes prior to development. This may prevent duplicate or unnecessary effort and allow other contributors to provide input. 7. All automated tests must pass. ## Contribution process If you are a new GitHub user, we recommend that you create your own [free github account](https://github.com/signup/free). -This will allow you to collaborate with the Magento 2 development team, fork the Magento 2 project and send pull requests. +This will allow you to collaborate with the Mage-OS Magento 2 development team, fork the Mage-OS Magento 2 project and send pull requests. -1. Search current [listed issues](https://github.com/magento/magento2/issues) (open or closed) for similar proposals of intended contribution before starting work on a new contribution. +1. Search current [listed issues](https://github.com/mage-os/mageos-magento2/issues) (open or closed) for similar proposals of intended contribution before starting work on a new contribution. 2. Review the [Contributor License Agreement](https://opensource.adobe.com/cla.html) if this is your first time contributing. 3. Create and test your work. -4. Follow the [Forks And Pull Requests Instructions](https://developer.adobe.com/commerce/contributor/guides/code-contributions/) to fork the Magento 2 repository and send us a pull request. -5. Once your contribution is received the Magento 2 development team will review the contribution and collaborate with you as needed. +4. Follow the [Forks And Pull Requests Instructions](https://developer.adobe.com/commerce/contributor/guides/code-contributions/) to fork the Mage-OS Magento 2 repository and send us a pull request. +5. Once your contribution is received the Mage-OS Magento 2 development team will review the contribution and collaborate with you as needed. ## Code of Conduct diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 2b1720ccaaba..bfa885e59552 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,5 +1,5 @@ 1. 2. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index d4e0707f7ede..71e60ce0f473 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -1,5 +1,5 @@ name: Bug report -description: Technical issue with the Magento 2 core components +description: Technical issue with the Mage-OS Magento 2 core components body: - type: markdown attributes: @@ -12,7 +12,7 @@ body: Describe your environment. Provide all the details that will help us to reproduce the bug. value: | - - Magento version + - Mage-OS Magento version - Anything else that would help a developer reproduce the bug - type: textarea attributes: @@ -51,7 +51,7 @@ body: attributes: label: Additional information description: | - Additional information is often requested when the bug report is processed. You can save time by providing both Magento and browser logs, screenshots, repository branch and HEAD commit you checked out to install Magento and any other artifacts related to the issue. + Additional information is often requested when the bug report is processed. You can save time by providing both Mage-OS Magento and browser logs, screenshots, repository branch and HEAD commit you checked out to install Magento and any other artifacts related to the issue. Also, links to the comments with important information, Root Cause analysis, additional video recordings; and anything else that is important for the issue and at some reason cannot be added to other sections. - type: textarea attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index b6b7491ada86..3c0a463e44d8 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -5,7 +5,7 @@ body: - type: markdown attributes: value: | - Important: This repository is intended only for Magento 2 Technical Issues. + Important: This repository is intended only for Mage-OS Magento 2 Technical Issues. Enter Feature Requests at https://github.com/magento/community-features. Project stakeholders monitor and manage requests. Feature requests entered using this form may be moved to the forum. @@ -28,7 +28,7 @@ body: attributes: label: Benefits description: | - How do you think this feature would improve Magento? + How do you think this feature would improve Mage-OS Magento? - type: textarea attributes: label: Additional information diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fd2f7f1badc2..5dc88d3d9b01 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,5 @@ -1. Fixes magento/magento2# +1. Fixes mage-os/mageos-magento2# ### Manual testing scenarios (*) + Magento\AdminAnalytics\ViewModel\Metadata diff --git a/app/code/Magento/Backend/Block/Page/Footer.php b/app/code/Magento/Backend/Block/Page/Footer.php index d477b357bf55..58996c2ac9f6 100644 --- a/app/code/Magento/Backend/Block/Page/Footer.php +++ b/app/code/Magento/Backend/Block/Page/Footer.php @@ -5,6 +5,8 @@ */ namespace Magento\Backend\Block\Page; +use Magento\Framework\App\DistributionMetadataInterface; + /** * Adminhtml footer block * @@ -19,7 +21,7 @@ class Footer extends \Magento\Backend\Block\Template protected $_template = 'Magento_Backend::page/footer.phtml'; /** - * @var \Magento\Framework\App\ProductMetadataInterface + * @var \Magento\Framework\App\ProductMetadataInterface|DistributionMetadataInterface * @since 100.1.0 */ protected $productMetadata; @@ -54,7 +56,17 @@ protected function _construct() */ public function getMagentoVersion() { - return $this->productMetadata->getVersion(); + return $this->productMetadata->getDistributionVersion(); + } + + /** + * Get product name + * + * @return string + */ + public function getName() + { + return $this->productMetadata->getDistributionName(); } /** diff --git a/app/code/Magento/Backend/i18n/en_US.csv b/app/code/Magento/Backend/i18n/en_US.csv index e2333f19b2fa..adf041282841 100644 --- a/app/code/Magento/Backend/i18n/en_US.csv +++ b/app/code/Magento/Backend/i18n/en_US.csv @@ -240,7 +240,10 @@ password,password "Reload Data","Reload Data" "Browse Files...","Browse Files..." Magento,Magento +"Mage-OS","Mage-OS" "Copyright © %1 Magento Commerce Inc. All rights reserved.","Copyright © %1 Magento Commerce Inc. All rights reserved." +"Thank you for choosing Mage-OS.","Thank you for choosing Mage-OS." +"Learn more about Mage-OS.","Learn more about Mage-OS." "ver. %1","ver. %1" "Magento Admin Panel","Magento Admin Panel" "Account Setting","Account Setting" diff --git a/app/code/Magento/Backend/view/adminhtml/layout/admin_login.xml b/app/code/Magento/Backend/view/adminhtml/layout/admin_login.xml index a90fbb1a0fca..ec89f74f7b1b 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/admin_login.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/admin_login.xml @@ -24,7 +24,7 @@ - images/magento-logo.svg + images/mage-os-logo.svg diff --git a/app/code/Magento/Backend/view/adminhtml/layout/default.xml b/app/code/Magento/Backend/view/adminhtml/layout/default.xml index 0ac358aa1437..375e78ae4cbc 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/default.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/default.xml @@ -25,7 +25,7 @@ logo Community Edition - images/magento-icon.svg + images/mage-os-icon.svg @@ -62,12 +62,12 @@ - https://www.adobe.com/privacy/policy.html + https://mage-os.org/privacy-policy - https://github.com/magento/magento2/issues + https://github.com/mage-os/mageos-magento2/issues diff --git a/app/code/Magento/Backend/view/adminhtml/templates/page/copyright.phtml b/app/code/Magento/Backend/view/adminhtml/templates/page/copyright.phtml index 1df1b076c2cf..a9c2b186e7cc 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/page/copyright.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/page/copyright.phtml @@ -3,6 +3,17 @@ * Copyright 2014 Adobe * All Rights Reserved. */ + +use Magento\Backend\Block\Page\Copyright; +use Magento\Framework\Escaper; + +/** @var Escaper $escaper */ +/** @var Copyright $block */ ?> - -escapeHtml(__('Copyright © %1 Magento Commerce Inc. All rights reserved.', date('Y'))) ?> +escapeHtml(__('Thank you for choosing Mage-OS.')); ?> + + escapeHtml(__('Learn more about Mage-OS.')); ?> + diff --git a/app/code/Magento/Backend/view/adminhtml/templates/page/footer.phtml b/app/code/Magento/Backend/view/adminhtml/templates/page/footer.phtml index 3e2856169f70..ceda016fa731 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/page/footer.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/page/footer.phtml @@ -3,8 +3,14 @@ * Copyright 2014 Adobe * All Rights Reserved. */ + +use Magento\Backend\Block\Page\Footer; +use Magento\Framework\Escaper; + +/** @var Escaper $escaper */ +/** @var Footer $block */ ?>

- escapeHtml(__('Magento')) ?> - escapeHtml(__('ver. %1', $block->getMagentoVersion())) ?> + escapeHtml(__($block->getName())); ?> + escapeHtml(__('ver. %1', $block->getMagentoVersion())); ?>

diff --git a/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml b/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml index 82e9950cdffb..9ad68fb9f0aa 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/page/header.phtml @@ -3,62 +3,74 @@ * Copyright 2014 Adobe * All Rights Reserved. */ +declare(strict_types=1); -/** @var $block \Magento\Backend\Block\Page\Header */ +use Magento\Backend\Block\Page\Header; +use Magento\Framework\Escaper; + +/** @var Escaper $escaper */ +/** @var Header $block */ $part = $block->getShowPart(); ?> - - hasEdition() ? 'data-edition="' . $block->escapeHtml($block->getEdition()) . '"' : ''; ?> - hasLogoImageSrc()) ? $block->escapeHtml($block->getLogoImageSrc()) : 'images/magento-logo.svg' ?> - - class="logo"> - <?= $block->escapeHtml(__('Magento Admin Panel')) ?> - - - + + getChildHtml(); ?> diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Option/AreBundleOptionsSalable.php b/app/code/Magento/Bundle/Model/ResourceModel/Option/AreBundleOptionsSalable.php index c9617c16338b..dc12560003a3 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Option/AreBundleOptionsSalable.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Option/AreBundleOptionsSalable.php @@ -72,6 +72,10 @@ public function execute(int $entityId, int $storeId): bool ['child_products' => $this->resourceConnection->getTableName('catalog_product_entity')], 'child_products.entity_id = bundle_selections.product_id', [] + )->joinInner( + ['child_stock_item' => $this->resourceConnection->getTableName('cataloginventory_stock_item')], + 'child_stock_item.product_id = child_products.entity_id', + [] )->group( ['bundle_options.parent_id', 'bundle_options.option_id'] )->where( @@ -103,18 +107,36 @@ public function execute(int $entityId, int $storeId): bool '1', '0' ); + + $hasMinRequiredQuantity = $connection->getCheckSql( + 'required = 1 AND manage_stock = 1 AND selection_can_change_qty = 0', + '(qty >= bundle_selections.selection_qty OR backorders > 0) AND is_in_stock = 1', + '1' + ); + + $requiredInStock = $connection->getCheckSql( + 'required = 1 AND manage_stock = 1 AND selection_can_change_qty = 1', + '(qty >= 0 OR backorders > 0) AND is_in_stock = 1', + '1' + ); + $optionsSaleabilitySelect->columns([ 'required' => 'bundle_options.required', 'is_salable' => $isOptionSalableExpr, 'is_required_and_unsalable' => $isRequiredOptionUnsalable, + 'has_min_required_quantity' => $hasMinRequiredQuantity, + 'required_in_stock' => $requiredInStock ]); $select = $connection->select()->from( $optionsSaleabilitySelect, - [new \Zend_Db_Expr('(MAX(is_salable) = 1 AND MAX(is_required_and_unsalable) = 0)')] + [new \Zend_Db_Expr( + '(MAX(is_salable) = 1 AND MAX(is_required_and_unsalable) = 0)' . + 'AND MIN(required_in_stock) = 1 AND MIN(has_min_required_quantity) = 1' + )] ); - $isSalable = $connection->fetchOne($select); + $isSalable = $connection->fetchOne($select); return (bool) $isSalable; } } diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php index 9b99ab5b4b13..fd8db1fc53c0 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php @@ -162,7 +162,9 @@ public function lockProductsStock(array $productIds, $websiteId) $items[$si['product_id']] = $si; } foreach ($this->getConnection()->fetchAll($selectProducts) as $p) { - $items[$p['product_id']]['type_id'] = $p['type_id']; + if (isset($items[$p['product_id']])) { + $items[$p['product_id']]['type_id'] = $p['type_id']; + } } return $items; diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item/StockItemCriteria.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item/StockItemCriteria.php index b93ff611c24a..18324a099ae0 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item/StockItemCriteria.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item/StockItemCriteria.php @@ -55,7 +55,11 @@ public function setScopeFilter($scope) */ public function setProductsFilter($products) { - $this->data['products_filter'] = [$products]; + if (is_array($products)) { + $this->data['products_filter'] = $products; + } else { + $this->data['products_filter'] = [$products]; + } return true; } diff --git a/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php b/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php index 5815a027828b..433fc7e09b53 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php @@ -36,25 +36,40 @@ public function getConfig(DataObject $config) : DataObject { $config->addData([ 'tinymce' => [ - 'toolbar' => ' blocks fontfamily fontsizeinput| formatselect | bold italic underline ' . - '| alignleft aligncenter alignright | bullist numlist | link table charmap', + 'toolbar' => 'blocks | fontfamily fontsizeinput lineheight | forecolor backcolor | ' + . 'bold italic underline | alignleft aligncenter alignright alignjustify | ' + . 'bullist numlist | link image', 'plugins' => implode( ' ', [ - 'advlist', + 'anchor', 'autolink', - 'lists', - 'link', 'charmap', - 'media', - 'table', 'code', + 'codesample', + 'directionality', + 'emoticons', 'help', - 'table' + 'image', + 'link', + 'lists', + 'media', + 'nonbreaking', + 'preview', + 'table', + 'visualblocks', + 'visualchars', + 'advlist', ] ), - 'content_css' => $this->assetRepo->getUrl('mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css') - ] + 'content_css' => $this->assetRepo->getUrl('mage/adminhtml/wysiwyg/tiny_mce/themes/ui.css'), + ], + 'settings' => [ + 'menubar' => 'edit insert view format table help', + 'statusbar' => false, + 'image_advtab' => true, + 'promotion' => false, + ], ]); return $config; } diff --git a/app/code/Magento/ConfigurableImportExport/Model/Export/RowCustomizer.php b/app/code/Magento/ConfigurableImportExport/Model/Export/RowCustomizer.php index 05da3caa3386..91a0b7bbda44 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Export/RowCustomizer.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Export/RowCustomizer.php @@ -67,25 +67,32 @@ public function prepareData($collection, $productIds) { $productCollection = clone $collection; $productCollection->addAttributeToFilter('entity_id', ['in' => $productIds]) - ->addAttributeToFilter('type_id', ['eq' => ConfigurableProductType::TYPE_CODE]); + ->addAttributeToFilter('type_id', ['eq' => ConfigurableProductType::TYPE_CODE]); // set global scope during export $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); while ($product = $productCollection->fetchItem()) { - $productAttributesOptions = $product->getTypeInstance()->getConfigurableOptions($product); + /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $configurableInstance */ + $configurableInstance = $product->getTypeInstance(); + $productAttributesOptions = $configurableInstance->getConfigurableOptions($product); $this->configurableData[$product->getId()] = []; $variations = []; $variationsLabels = []; - foreach ($productAttributesOptions as $productAttributeOption) { - foreach ($productAttributeOption as $optValues) { - $variations[$optValues['sku']][] = $optValues['attribute_code'] . '=' . $optValues['option_title']; + /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute[] $superAttributes */ + $superAttributes = $configurableInstance->getUsedProductAttributes($product); + foreach ($superAttributes as $superAttribute) { + $code = $superAttribute->getAttributeCode(); + $variationsLabels[$code] = $code . '=' . $superAttribute->getDefaultFrontendLabel(); + } - if (!empty($optValues['super_attribute_label'])) { - $variationsLabels[$optValues['attribute_code']] = $optValues['attribute_code'] . '=' - . $optValues['super_attribute_label']; - } + /** @var \Magento\Catalog\Model\Product[] $childProducts */ + $childProducts = $configurableInstance->getUsedProducts($product); + foreach ($childProducts as $childProduct) { + foreach ($superAttributes as $superAttribute) { + $code = $superAttribute->getAttributeCode(); + $variations[$childProduct->getSku()][] = $code . '=' . $childProduct->getAttributeText($code); } } diff --git a/app/code/Magento/ConfigurableProduct/Model/Inventory/ChangeParentStockStatus.php b/app/code/Magento/ConfigurableProduct/Model/Inventory/ChangeParentStockStatus.php index 556949e77571..29e9f709e1dc 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Inventory/ChangeParentStockStatus.php +++ b/app/code/Magento/ConfigurableProduct/Model/Inventory/ChangeParentStockStatus.php @@ -59,7 +59,7 @@ public function __construct( /** * Update stock status of configurable products based on children products stock status * - * @param array $childrenIds + * @param array $childrenIds * @return void */ public function execute(array $childrenIds): void @@ -70,6 +70,17 @@ public function execute(array $childrenIds): void } } + /** + * Updates the parent stock status based on children statuses + * + * @param int $parentId + * @return void + */ + public function executeFromParent(int $parentId): void + { + $this->processStockForParent($parentId); + } + /** * Update stock status of configurable product based on children products stock status * @@ -106,6 +117,7 @@ private function processStockForParent(int $productId): void if ($this->isNeedToUpdateParent($parentStockItem, $childrenIsInStock)) { $parentStockItem->setIsInStock($childrenIsInStock); $parentStockItem->setStockStatusChangedAuto(1); + // @phpstan-ignore method.notFound $parentStockItem->setStockStatusChangedAutomaticallyFlag(true); $this->stockItemRepository->save($parentStockItem); } diff --git a/app/code/Magento/ConfigurableProduct/Model/Inventory/ParentItemProcessor.php b/app/code/Magento/ConfigurableProduct/Model/Inventory/ParentItemProcessor.php index 4359100ec6bf..343cb0185efb 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Inventory/ParentItemProcessor.php +++ b/app/code/Magento/ConfigurableProduct/Model/Inventory/ParentItemProcessor.php @@ -7,6 +7,7 @@ namespace Magento\ConfigurableProduct\Model\Inventory; +use Magento\Catalog\Model\Product\Type; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; use Magento\Catalog\Api\Data\ProductInterface as Product; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; @@ -20,10 +21,7 @@ */ class ParentItemProcessor implements ParentItemProcessorInterface { - /** - * @var ChangeParentStockStatus - */ - private $changeParentStockStatus; + private ChangeParentStockStatus $changeParentStockStatus; /** * @param Configurable $configurableType @@ -50,8 +48,12 @@ public function __construct( * @param Product $product * @return void */ - public function process(Product $product) + public function process(Product $product): void { - $this->changeParentStockStatus->execute([$product->getId()]); + if ($product->getTypeId() === Type::TYPE_SIMPLE) { + $this->changeParentStockStatus->execute([$product->getId()]); + } elseif ($product->getTypeId() === Configurable::TYPE_CODE) { + $this->changeParentStockStatus->executeFromParent((int)$product->getId()); + } } } diff --git a/app/code/Magento/Customer/Helper/Address.php b/app/code/Magento/Customer/Helper/Address.php index b8d865a7caca..840d25fd5473 100644 --- a/app/code/Magento/Customer/Helper/Address.php +++ b/app/code/Magento/Customer/Helper/Address.php @@ -330,7 +330,7 @@ public function convertStreetLines($origStreets, $toCount) } } - return $lines; + return array_filter($lines); } /** diff --git a/app/code/Magento/Customer/Model/Address.php b/app/code/Magento/Customer/Model/Address.php index 262264cfd955..05d48a0e961d 100644 --- a/app/code/Magento/Customer/Model/Address.php +++ b/app/code/Magento/Customer/Model/Address.php @@ -13,6 +13,7 @@ use Magento\Customer\Model\Address\AbstractAddress\RegionModelsCache; use Magento\Customer\Model\Address\CompositeValidator; use Magento\Framework\Indexer\StateInterface; +use Magento\Customer\Helper\Address as AddressHelper; /** * Customer address model @@ -80,6 +81,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress * @param CompositeValidator|null $compositeValidator * @param CountryModelsCache|null $countryModelsCache * @param RegionModelsCache|null $regionModelsCache + * @param AddressHelper|null $addressHelper * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -106,6 +108,7 @@ public function __construct( ?CompositeValidator $compositeValidator = null, ?CountryModelsCache $countryModelsCache = null, ?RegionModelsCache $regionModelsCache = null, + ?AddressHelper $addressHelper = null ) { $this->dataProcessor = $dataProcessor; $this->_customerFactory = $customerFactory; @@ -130,6 +133,7 @@ public function __construct( $compositeValidator, $countryModelsCache, $regionModelsCache, + $addressHelper ); } diff --git a/app/code/Magento/Customer/Model/Address/AbstractAddress.php b/app/code/Magento/Customer/Model/Address/AbstractAddress.php index 3f898d40d817..e6ab2504e3d9 100644 --- a/app/code/Magento/Customer/Model/Address/AbstractAddress.php +++ b/app/code/Magento/Customer/Model/Address/AbstractAddress.php @@ -17,6 +17,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Model\AbstractExtensibleModel; use Magento\Framework\ObjectManager\ResetAfterRequestInterface; +use Magento\Customer\Helper\Address as AddressHelper; /** * Address abstract model @@ -146,6 +147,11 @@ class AbstractAddress extends AbstractExtensibleModel implements AddressModelInt */ private array $regionIdCountry = []; + /** + * @var AddressHelper|null + */ + protected ?AddressHelper $addressHelper; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -166,6 +172,7 @@ class AbstractAddress extends AbstractExtensibleModel implements AddressModelInt * @param CompositeValidator $compositeValidator * @param CountryModelsCache|null $countryModelsCache * @param RegionModelsCache|null $regionModelsCache + * @param AddressHelper|null $addressHelper * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -189,6 +196,7 @@ public function __construct( ?CompositeValidator $compositeValidator = null, ?CountryModelsCache $countryModelsCache = null, ?RegionModelsCache $regionModelsCache = null, + ?AddressHelper $addressHelper = null ) { $this->_directoryData = $directoryData; $data = $this->_implodeArrayField($data); @@ -206,6 +214,8 @@ public function __construct( ->get(CountryModelsCache::class); $this->regionModelsCache = $regionModelsCache ?: ObjectManager::getInstance() ->get(RegionModelsCache::class); + $this->addressHelper = $addressHelper ?: ObjectManager::getInstance() + ->get(AddressHelper::class); parent::__construct( $context, $registry, @@ -242,6 +252,8 @@ public function getName() /** * Retrieve street field of an address + * Honour current configured street lines, and convert + * legacy data to match * * @return string[] */ @@ -250,7 +262,11 @@ public function getStreet() if (is_array($this->getStreetFull())) { return $this->getStreetFull(); } - return explode("\n", $this->getStreetFull()); + $maxAllowedLineCount = $this->addressHelper->getStreetLines() ?? 2; + $lines = explode("\n", $this->getStreetFull()); + $lines = $this->addressHelper->convertStreetLines($lines, $maxAllowedLineCount); + $this->setStreetFull(implode("\n", $lines)); + return $lines; } /** diff --git a/app/code/Magento/Customer/Model/Metadata/Form/Multiline.php b/app/code/Magento/Customer/Model/Metadata/Form/Multiline.php index 932d85d88de9..bf7c35e39eef 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/Multiline.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/Multiline.php @@ -116,7 +116,7 @@ public function outputValue($format = ElementFactory::OUTPUT_FORMAT_TEXT) $output = implode("
", $values); break; case ElementFactory::OUTPUT_FORMAT_ONELINE: - $output = implode(" ", $values); + $output = trim(implode(" ", $values), ' '); break; default: $output = implode("\n", $values); diff --git a/app/code/Magento/Customer/Model/ResourceModel/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Grid/Collection.php index e1060f2c38cd..71044a0b70ab 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Grid/Collection.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Grid/Collection.php @@ -65,7 +65,6 @@ protected function _initSelect() { parent::_initSelect(); $this->joinRegionNameTable(); - return $this; } diff --git a/app/code/Magento/Customer/Test/Unit/Helper/AddressTest.php b/app/code/Magento/Customer/Test/Unit/Helper/AddressTest.php index 0945d7b8c561..36c378223fe7 100644 --- a/app/code/Magento/Customer/Test/Unit/Helper/AddressTest.php +++ b/app/code/Magento/Customer/Test/Unit/Helper/AddressTest.php @@ -242,6 +242,7 @@ public static function getConvertStreetLinesDataProvider() return [ [['street1', 'street2', 'street3', 'street4'], 3, ['street1 street2', 'street3', 'street4']], [['street1', 'street2', 'street3', 'street4'], 2, ['street1 street2', 'street3 street4']], + [['street1', ''], 2, ['street1']] ]; } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/AbstractAddressTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/AbstractAddressTest.php index 6b4dc0297d1c..33226580f152 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Address/AbstractAddressTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/AbstractAddressTest.php @@ -70,6 +70,11 @@ class AbstractAddressTest extends TestCase /** @var CompositeValidator|MockObject */ private $compositeValidatorMock; + /** + * @var \Magento\Customer\Helper\Address|MockObject + */ + private $addressHelperMock; + protected function setUp(): void { $this->contextMock = $this->createMock(Context::class); @@ -491,6 +496,89 @@ function ($data) { ); } + public function testGetStreetWithTwoLines() + { + // Create a partial mock for AddressHelper + $this->addressHelperMock = $this->getMockBuilder(\Magento\Customer\Helper\Address::class) + ->disableOriginalConstructor() + ->onlyMethods(['getStreetLines']) // Mock only getStreetLines, keep the real convertStreetLines + ->getMock(); + + // Mock getStreetLines to return 2 by default + $this->addressHelperMock->method('getStreetLines')->willReturn(2); + + // Use reflection to inject the partial mock into the model + $reflection = new \ReflectionClass($this->model); + $property = $reflection->getProperty('addressHelper'); + $property->setAccessible(true); + $property->setValue($this->model, $this->addressHelperMock); + + $this->addressHelperMock->method('getStreetLines')->willReturn(2); + $streetData = ["Street Line 1", "Street Line 2", "Street Line 3", "Street Line 4"]; + $this->model->setData('street', $streetData); + + // Call getStreet() which should internally call convertStreetLines() + $result = $this->model->getStreet(); + + // Assert that empty and whitespace-only lines are removed by convertStreetLines + $this->assertEquals(["Street Line 1 Street Line 2", "Street Line 3 Street Line 4"], $result); + } + + public function testGetStreetWithThreeLines() + { + // Create a partial mock for AddressHelper + $this->addressHelperMock = $this->getMockBuilder(\Magento\Customer\Helper\Address::class) + ->disableOriginalConstructor() + ->onlyMethods(['getStreetLines']) // Mock only getStreetLines, keep the real convertStreetLines + ->getMock(); + + // Mock getStreetLines to return 2 by default + $this->addressHelperMock->method('getStreetLines')->willReturn(3); + + // Use reflection to inject the partial mock into the model + $reflection = new \ReflectionClass($this->model); + $property = $reflection->getProperty('addressHelper'); + $property->setAccessible(true); + $property->setValue($this->model, $this->addressHelperMock); + + $this->addressHelperMock->method('getStreetLines')->willReturn(3); + $streetData = ["Street Line 1", "Street Line 2", "Street Line 3", "Street Line 4"]; + $this->model->setData('street', $streetData); + + // Call getStreet() which should internally call convertStreetLines() + $result = $this->model->getStreet(); + + // Assert that empty and whitespace-only lines are removed by convertStreetLines + $this->assertEquals(["Street Line 1 Street Line 2","Street Line 3","Street Line 4"], $result); + } + + public function testGetStreetWithOneLine() + { + // Create a partial mock for AddressHelper + $this->addressHelperMock = $this->getMockBuilder(\Magento\Customer\Helper\Address::class) + ->disableOriginalConstructor() + ->onlyMethods(['getStreetLines']) // Mock only getStreetLines, keep the real convertStreetLines + ->getMock(); + + // Mock getStreetLines to return 2 by default + $this->addressHelperMock->method('getStreetLines')->willReturn(1); + + // Use reflection to inject the partial mock into the model + $reflection = new \ReflectionClass($this->model); + $property = $reflection->getProperty('addressHelper'); + $property->setAccessible(true); + $property->setValue($this->model, $this->addressHelperMock); + + $streetData = ["Street Line 1", "Street Line 2", "Street Line 3", "Street Line 4"]; + $this->model->setData('street', $streetData); + + // Call getStreet() which should internally call convertStreetLines() + $result = $this->model->getStreet(); + + // Assert that empty and whitespace-only lines are removed by convertStreetLines + $this->assertEquals(["Street Line 1 Street Line 2 Street Line 3 Street Line 4"], $result); + } + protected function tearDown(): void { $this->objectManager->setBackwardCompatibleProperty( diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index 00a8597d8c36..14f612c7ae4f 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -88,6 +88,7 @@ Magento\Customer\Model\Address\Config\Proxy + Magento\Customer\Helper\Address @@ -593,6 +594,11 @@ type="Magento\Customer\Plugin\AsyncRequestCustomerGroupAuthorization" /> + + + Magento\Customer\Helper\Address + + customer_grid diff --git a/app/code/Magento/Elasticsearch/Model/DataProvider/Base/Suggestions.php b/app/code/Magento/Elasticsearch/Model/DataProvider/Base/Suggestions.php index 0098d0a1e41a..04749a8e7bf4 100644 --- a/app/code/Magento/Elasticsearch/Model/DataProvider/Base/Suggestions.php +++ b/app/code/Magento/Elasticsearch/Model/DataProvider/Base/Suggestions.php @@ -8,7 +8,9 @@ use Elasticsearch\Common\Exceptions\BadRequest400Exception; use Exception; use Magento\AdvancedSearch\Model\SuggestedQueriesInterface; +use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; +use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; @@ -61,6 +63,16 @@ class Suggestions implements SuggestedQueriesInterface */ private $fieldProvider; + /** + * @var CollectionFactory + */ + private $productAttributeCollectionFactory; + + /** + * @var FieldMapperInterface + */ + private $fieldMapper; + /** * @var LoggerInterface */ @@ -90,6 +102,8 @@ class Suggestions implements SuggestedQueriesInterface * @param FieldProviderInterface $fieldProvider * @param LoggerInterface|null $logger * @param GetSuggestionFrequencyInterface|null $getSuggestionFrequency + * @param CollectionFactory|null $productAttributeCollectionFactory + * @param FieldMapperInterface|null $fieldMapper * @param array $responseErrorExceptionList * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -103,6 +117,8 @@ public function __construct( FieldProviderInterface $fieldProvider, ?LoggerInterface $logger = null, ?GetSuggestionFrequencyInterface $getSuggestionFrequency = null, + ?CollectionFactory $productAttributeCollectionFactory = null, + ?FieldMapperInterface $fieldMapper = null, array $responseErrorExceptionList = [] ) { $this->queryResultFactory = $queryResultFactory; @@ -111,10 +127,13 @@ public function __construct( $this->config = $config; $this->searchIndexNameResolver = $searchIndexNameResolver; $this->storeManager = $storeManager; - $this->fieldProvider = $fieldProvider; $this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class); $this->getSuggestionFrequency = $getSuggestionFrequency ?: ObjectManager::getInstance()->get(GetSuggestionFrequencyInterface::class); + $this->productAttributeCollectionFactory = $productAttributeCollectionFactory ?: + ObjectManager::getInstance()->get(CollectionFactory::class); + $this->fieldMapper = $fieldMapper ?: + ObjectManager::getInstance()->get(FieldMapperInterface::class); $this->responseErrorExceptionList = array_merge($this->responseErrorExceptionList, $responseErrorExceptionList); } @@ -281,11 +300,20 @@ private function addSuggestFields($searchQuery, $searchSuggestionsCount) */ private function getSuggestFields() { - $fields = array_filter($this->fieldProvider->getFields(), function ($field) { - return (($field['type'] ?? null) === 'text') && (($field['index'] ?? null) !== false); - }); - - return array_keys($fields); + $productAttributes = $this->productAttributeCollectionFactory->create(); + $productAttributes->addFieldToFilter( + 'is_searchable', + 1 + ); + $attributeCodes = $productAttributes->getColumnValues('attribute_code'); + $fields = []; + foreach ($attributeCodes as $attributeCode) { + $fields[] = $this->fieldMapper->getFieldName( + $attributeCode, + ['type' => FieldMapperInterface::TYPE_QUERY] + ); + } + return $fields; } /** diff --git a/app/code/Magento/Email/view/frontend/web/logo_email.png b/app/code/Magento/Email/view/frontend/web/logo_email.png index 215e9d06edcd..ac822941c785 100644 Binary files a/app/code/Magento/Email/view/frontend/web/logo_email.png and b/app/code/Magento/Email/view/frontend/web/logo_email.png differ diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/templates/html/notices.phtml b/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/templates/html/notices.phtml index 085a4a7da211..e2f4ecfdb5da 100644 --- a/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/templates/html/notices.phtml +++ b/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/templates/html/notices.phtml @@ -8,7 +8,7 @@ * @var \Magento\Framework\View\Element\Template $block * @var \Magento\Framework\Escaper $escaper */ -$viewFileUrl = $block->getViewFileUrl('Magento_LoginAsCustomerFrontendUi::images/magento-icon.svg'); +$viewFileUrl = $block->getViewFileUrl('Magento_LoginAsCustomerFrontendUi::images/mage-os-icon.svg'); ?> getConfig()->isEnabled()): ?>
getViewFileUrl('Magento_LoginAsCustomerFrontendUi::images