diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php index e054a9d49b437..817de6828e48d 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php @@ -69,6 +69,7 @@ class Save extends Attribute * @var LayoutFactory */ private $layoutFactory; + /** * @var Presentation */ @@ -124,6 +125,7 @@ public function execute() { $data = $this->getRequest()->getPostValue(); if ($data) { + $this->preprocessOptionsData($data); $setId = $this->getRequest()->getParam('set'); $attributeSet = null; @@ -313,6 +315,28 @@ public function execute() return $this->returnResult('catalog/*/', [], ['error' => true]); } + /** + * Extract options data from serialized options field and append to data array. + * + * This logic is required to overcome max_input_vars php limit + * that may vary and/or be inaccessible to change on different instances. + * + * @param array $data + * @return void + */ + private function preprocessOptionsData(&$data) + { + if (isset($data['serialized_options'])) { + $serializedOptions = json_decode($data['serialized_options'], JSON_OBJECT_AS_ARRAY); + foreach ($serializedOptions as $serializedOption) { + $option = []; + parse_str($serializedOption, $option); + $data = array_replace_recursive($data, $option); + } + } + unset($data['serialized_options']); + } + /** * @param string $path * @param array $params diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/options.js b/app/code/Magento/Catalog/view/adminhtml/web/js/options.js index 787516a9abf29..6ea005915763c 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/options.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/options.js @@ -13,12 +13,16 @@ define([ 'jquery/ui', 'prototype', 'form', - 'validation' + 'validation', + 'mage/translate' ], function (jQuery, mageTemplate, rg) { 'use strict'; return function (config) { - var attributeOption = { + var optionPanel = jQuery('#manage-options-panel'), + optionsValues = [], + editForm = jQuery('#edit_form'), + attributeOption = { table: $('attribute-options-table'), itemCount: 0, totalItems: 0, @@ -150,7 +154,7 @@ define([ attributeOption.remove(event); }); - jQuery('#manage-options-panel').on('render', function () { + optionPanel.on('render', function () { attributeOption.ignoreValidate(); if (attributeOption.rendered) { @@ -176,7 +180,31 @@ define([ }); }); } + editForm.on('submit', function () { + optionPanel.find('input') + .each(function () { + if (this.disabled) { + return; + } + if (this.type === 'checkbox' || this.type === 'radio') { + if (this.checked) { + optionsValues.push(this.name + '=' + jQuery(this).val()); + } + } else { + optionsValues.push(this.name + '=' + jQuery(this).val()); + } + }); + jQuery('') + .attr({ + type: 'hidden', + name: 'serialized_options' + }) + .val(JSON.stringify(optionsValues)) + .prependTo(editForm); + optionPanel.find('table') + .replaceWith(jQuery('
').text(jQuery.mage.__('Sending attribute values as package.'))); + }); window.attributeOption = attributeOption; window.optionDefaultInputType = attributeOption.getOptionInputType(); diff --git a/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php b/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php index dfda76372df1f..383c97a166d34 100644 --- a/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php +++ b/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php @@ -11,7 +11,7 @@ use Magento\Swatches\Model\Swatch; /** - * Class Save + * Plugin for product attribute save controller. */ class Save { @@ -24,7 +24,17 @@ class Save public function beforeDispatch(Attribute\Save $subject, RequestInterface $request) { $data = $request->getPostValue(); + if (isset($data['frontend_input'])) { + //Data is serialized to overcome issues caused by max_input_vars value if it's modification is unavailable. + //See subject controller code and comments for more info. + if (isset($data['serialized_swatch_values']) + && in_array($data['frontend_input'], ['swatch_visual', 'swatch_text']) + ) { + $data['serialized_options'] = $data['serialized_swatch_values']; + unset($data['serialized_swatch_values']); + } + switch ($data['frontend_input']) { case 'swatch_visual': $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_VISUAL; diff --git a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js index 1187e6bc4fdaa..01411523108cf 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js +++ b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js @@ -414,6 +414,8 @@ define([ }; $(function () { + var editForm = $('#edit_form'); + $('#frontend_input').bind('change', function () { swatchProductAttributes.bindAttributeInputType(); }); @@ -427,6 +429,34 @@ define([ $('.attribute-popup .collapse, [data-role="advanced_fieldset-content"]') .collapsable() .collapse('hide'); + + editForm.on('submit', function () { + var activePanel, + swatchValues = [], + swatchVisualPanel = $('#swatch-visual-options-panel'), + swatchTextPanel = $('#swatch-text-options-panel'); + + activePanel = swatchTextPanel.is(':visible') ? swatchTextPanel : swatchVisualPanel; + + activePanel + .find('table input') + .each(function () { + swatchValues.push(this.name + '=' + $(this).val()); + }); + + $('') + .attr({ + type: 'hidden', + name: 'serialized_swatch_values' + }) + .val(JSON.stringify(swatchValues)) + .prependTo(editForm); + + [swatchVisualPanel, swatchTextPanel].forEach(function (el) { + $(el).find('table') + .replaceWith($('
').text($.mage.__('Sending swatch values as package.'))); + }); + }); }); window.saveAttributeInNewSet = swatchProductAttributes.saveAttributeInNewSet; diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_authentication.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_authentication.less index e1e23a9ffbb15..c5a26b3bc83ac 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_authentication.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_authentication.less @@ -25,6 +25,7 @@ padding: @block-auth__dropdown__padding; } } + .authentication-wrapper { float: right; margin-top: -1.5*@indent__xl; diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payment-options.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payment-options.less index 4e1156949de3a..3ce46a73a11c4 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payment-options.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payment-options.less @@ -198,6 +198,7 @@ .payment-option-title { .lib-css(padding-left, @checkout-payment-option-content__padding__xl); } + .payment-option-content { .payment-option-inner { + .actions-toolbar { diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_shipping.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_shipping.less index e7f0259fc9ce3..0a463a95e3182 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_shipping.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_shipping.less @@ -188,6 +188,7 @@ } } } + .row-error { td { border-top: none; @@ -285,6 +286,7 @@ .lib-css(max-width, @checkout-shipping-address__max-width); } } + .table-checkout-shipping-method { width: auto; } @@ -324,6 +326,7 @@ } } } + .table-checkout-shipping-method { min-width: 500px; } diff --git a/app/design/frontend/Magento/blank/Magento_GiftRegistry/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_GiftRegistry/web/css/source/_module.less index d05bcec38cbed..03a474012cb0c 100644 --- a/app/design/frontend/Magento/blank/Magento_GiftRegistry/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_GiftRegistry/web/css/source/_module.less @@ -15,6 +15,7 @@ .actions-toolbar:not(:last-child) { margin-bottom: @indent__xl; } + .fieldset { .nested { .field:not(.choice) { diff --git a/app/design/frontend/Magento/blank/Magento_Multishipping/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Multishipping/web/css/source/_module.less index 1e5769f3d7396..47dcd045ed46d 100644 --- a/app/design/frontend/Magento/blank/Magento_Multishipping/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Multishipping/web/css/source/_module.less @@ -35,13 +35,16 @@ .shipping-item { margin-left:84px; } + .shipping-label { font-weight: @font-weight__bold; margin-right: @indent__s; } + .shipping-address { font-weight: @font-weight__regular; } + .error-block { color: @color-red10; @@ -49,6 +52,7 @@ font-weight: @font-weight__bold; margin-right: @indent__s; } + .error-description { font-weight: @font-weight__regular; } @@ -64,6 +68,7 @@ .order-id { float:left; } + .shipping-item { margin-left:100px; } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_shipping.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_shipping.less index 512751df1cb35..d0ce87beb6ad3 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_shipping.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_shipping.less @@ -296,6 +296,7 @@ } } } + .opc-wrapper { .form-login, .form-shipping-address { @@ -307,6 +308,7 @@ padding-bottom: @indent__base; } } + .table-checkout-shipping-method { width: auto; } @@ -346,6 +348,7 @@ } } } + .table-checkout-shipping-method { min-width: 500px; } diff --git a/app/design/frontend/Magento/luma/Magento_InstantPurchase/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_InstantPurchase/web/css/source/_module.less index 9877f6bbcea23..230cce3a9aeff 100644 --- a/app/design/frontend/Magento/luma/Magento_InstantPurchase/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_InstantPurchase/web/css/source/_module.less @@ -1,16 +1,16 @@ & when (@media-common = true) { - .box-tocart { - .action.instant-purchase { - &:extend(.abs-button-l all); - &:extend(.abs-button-responsive all); + .box-tocart { + .action.instant-purchase { + &:extend(.abs-button-l all); + &:extend(.abs-button-responsive all); - &:not(:last-child) { - margin-bottom: 15px; - } + &:not(:last-child) { + margin-bottom: 15px; + } - vertical-align: top; + vertical-align: top; + } } - } } // @@ -18,11 +18,11 @@ // _____________________________________________ .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { - .box-tocart { - .action.instant-purchase { - margin-bottom: 0; - margin-right: 1%; - width: 49%; + .box-tocart { + .action.instant-purchase { + margin-bottom: 0; + margin-right: 1%; + width: 49%; + } } - } } diff --git a/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less index 03ff1fb09a3e4..4b5cd30a4a8ee 100644 --- a/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less @@ -8,285 +8,290 @@ // _____________________________________________ & when (@media-common = true) { - .multicheckout { - &.results, &.success { - h3 { - font-size: 1.6rem; - margin-bottom: @indent__base; - margin-top: @indent__l; - a { - color: @text__color; - &:hover { - text-decoration: none; - } - } - } - - ul.orders-list { - list-style: none; - padding-left: 0; - } - - .orders-list { - margin-top: @indent__m; - padding-left: @indent__base - 4px; - - .shipping-list { - .shipping-item { - margin-left:84px; - } - .shipping-label { - font-weight: @font-weight__bold; - margin-right: @indent__s; - } - .shipping-address { - font-weight: @font-weight__regular; - } - .error-block { - color: @color-red10; - .error-label { - font-weight: @font-weight__bold; - margin-right: @indent__s; + .multicheckout { + &.results, &.success { + h3 { + font-size: 1.6rem; + margin-bottom: @indent__base; + margin-top: @indent__l; + a { + color: @text__color; + &:hover { + text-decoration: none; + } } - .error-description { - font-weight: @font-weight__regular; + } + + ul.orders-list { + list-style: none; + padding-left: 0; + } + + .orders-list { + margin-top: @indent__m; + padding-left: @indent__base - 4px; + + .shipping-list { + .shipping-item { + margin-left:84px; + } + + .shipping-label { + font-weight: @font-weight__bold; + margin-right: @indent__s; + } + + .shipping-address { + font-weight: @font-weight__regular; + } + + .error-block { + color: @color-red10; + + .error-label { + font-weight: @font-weight__bold; + margin-right: @indent__s; + } + + .error-description { + font-weight: @font-weight__regular; + } + } } - } } - } - .orders-succeed { - .orders-list { - margin-top: 0; + .orders-succeed { + .orders-list { + margin-top: 0; + + .shipping-list { + .order-id { + float:left; + } + .shipping-item { + margin-left:100px; + } + } + } + } + } - .shipping-list { - .order-id { - float:left; - } - .shipping-item { - margin-left:100px; - } + .title { + margin-bottom: @indent__l; + + strong { + font-weight: @font-weight__regular; } - } } - } - .title { - margin-bottom: @indent__l; + .table-wrapper { + margin-bottom: 0; - strong { - font-weight: @font-weight__regular; - } - } + .action.delete { + display: inline-block; + } + + .col { + .qty { + display: inline-block; + + .input-text { + &:extend(.abs-input-qty all); + } + } - .table-wrapper { - margin-bottom: 0; + .label { + &:extend(.abs-visually-hidden all); + } - .action.delete { - display: inline-block; - } + &.item { + .action.edit { + font-weight: @font-weight__regular; + margin-left: @indent__s; + } + } + } - .col { - .qty { - display: inline-block; + .cart-price { + &:extend(.abs-checkout-cart-price all); + } - .input-text { - &:extend(.abs-input-qty all); - } + .product-item-name { + &:extend(.abs-checkout-product-name all); + } } - .label { - &:extend(.abs-visually-hidden all); + &:not(.address) { + .table-wrapper { + .product-item-name { + margin: 0; + } + } } - &.item { - .action.edit { - font-weight: @font-weight__regular; - margin-left: @indent__s; - } + > .actions-toolbar { + margin-top: @indent__xl; } - } - .cart-price { - &:extend(.abs-checkout-cart-price all); - } + .actions-toolbar { + > .secondary { + display: block; - .product-item-name { - &:extend(.abs-checkout-product-name all); - } - } + .action { + margin-bottom: @indent__m; - &:not(.address) { - .table-wrapper { - .product-item-name { - margin: 0; - } - } - } + &.back { + display: block; + margin-left: 0; + } + } + } - > .actions-toolbar { - margin-top: @indent__xl; - } + > .primary { + margin-right: @indent__s; + } + } - .actions-toolbar { - > .secondary { - display: block; + .action.primary { + &:extend(.abs-button-l all); + } - .action { - margin-bottom: @indent__m; + .item-options { + margin: @indent__s 0 0; - &.back { - display: block; - margin-left: 0; - } + &:extend(.abs-product-options-list all); + &:extend(.abs-add-clearfix all); } - } - > .primary { - margin-right: @indent__s; - } - } + &:extend(.abs-account-blocks all); - .action.primary { - &:extend(.abs-button-l all); - } + .block { + &:extend(.abs-add-clearfix all); - .item-options { - margin: @indent__s 0 0; - - &:extend(.abs-product-options-list all); - &:extend(.abs-add-clearfix all); - } + .methods-shipping { + .item-content { + .fieldset { + > .legend { + &:extend(.abs-visually-hidden all); + } - &:extend(.abs-account-blocks all); + > .legend + br { + &:extend(.abs-no-display all); + } - .block { - &:extend(.abs-add-clearfix all); + > .field { + &:before { + display: none; + } - .methods-shipping { - .item-content { - .fieldset { - > .legend { - &:extend(.abs-visually-hidden all); + .control { + display: inline-block; + } + } + } + } } + } - > .legend + br { - &:extend(.abs-no-display all); - } + .block-title, + .block-content .title { + &:extend(.abs-account-title all); + border-bottom: @border-width__base solid @border-color__base; + padding-bottom: @indent__s; - > .field { - &:before { - display: none; - } + strong { + font-weight: @font-weight__regular; - .control { - display: inline-block; - } + span { + .lib-css(color, @primary__color__light); + } } - } } - } - } - - .block-title, - .block-content .title { - &:extend(.abs-account-title all); - border-bottom: @border-width__base solid @border-color__base; - padding-bottom: @indent__s; - - strong { - font-weight: @font-weight__regular; - span { - .lib-css(color, @primary__color__light); + .block-content { + &:extend(.abs-add-clearfix all); + .title { + border-bottom: none; + padding-bottom: 0; + } } - } - } - .block-content { - &:extend(.abs-add-clearfix all); - .title { - border-bottom: none; - padding-bottom: 0; - } - } + &.order-review { + .block-title > strong { + .lib-font-size(24); + } - &.order-review { - .block-title > strong { - .lib-font-size(24); - } + .block-shipping { + .block-content:not(:last-child) { + margin-bottom: @indent__xl; + } + } - .block-shipping { - .block-content:not(:last-child) { - margin-bottom: @indent__xl; + .error-description { + color: @color-red10; + font-weight: @font-weight__regular; + margin-bottom: @indent__s; + margin-top: -@indent__s; + } } - } - .error-description { - color: @color-red10; - font-weight: @font-weight__regular; - margin-bottom: @indent__s; - margin-top: -@indent__s; - } - } - - .box-title { - span { - margin-right: @indent__s; - } + .box-title { + span { + margin-right: @indent__s; + } - > .action { - margin: 0; - } - } + > .action { + margin: 0; + } + } - .box-shipping-method { - .price { - font-weight: @font-weight__bold; - } - } + .box-shipping-method { + .price { + font-weight: @font-weight__bold; + } + } - .box-billing-method { - .fieldset { - margin: 0; + .box-billing-method { + .fieldset { + margin: 0; - .legend.box-title { - margin: 0 0 @indent__xs; + .legend.box-title { + margin: 0 0 @indent__xs; + } + } } - } - } - .hidden { - &:extend(.abs-no-display all); - } + .hidden { + &:extend(.abs-no-display all); + } - .checkout-review .grand.totals { - .lib-font-size(@font-size__xl); - margin-bottom: @indent__xl; + .checkout-review .grand.totals { + .lib-font-size(@font-size__xl); + margin-bottom: @indent__xl; - .mark { - font-weight: @font-weight__regular; - } + .mark { + font-weight: @font-weight__regular; + } + } } - } - [class^='multishipping-'] { - .nav-sections, - .nav-toggle { - &:extend(.abs-no-display all); - } + [class^='multishipping-'] { + .nav-sections, + .nav-toggle { + &:extend(.abs-no-display all); + } - .logo { - margin-left: 0; + .logo { + margin-left: 0; + } } - } - .multishipping-checkout-success { - .nav-sections { - display: block; + .multishipping-checkout-success { + .nav-sections { + display: block; + } } - } } // @@ -294,200 +299,200 @@ // _____________________________________________ .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { - .multicheckout { - .data.table { - .address { - &:before { - margin-bottom: @indent__xs; + .multicheckout { + .data.table { + .address { + &:before { + margin-bottom: @indent__xs; + } + } } - } - } - - .product-item-name, - .price-including-tax, - .price-excluding-tax { - display: inline-block; - } - .block-content .box { - &:not(:last-child) { - margin-bottom: @indent__xl; - } + .product-item-name, + .price-including-tax, + .price-excluding-tax { + display: inline-block; + } - &:last-child { - margin-bottom: 0; - } - } + .block-content .box { + &:not(:last-child) { + margin-bottom: @indent__xl; + } - &.order-review { - .box-items { - .data.table { - thead { - display: block; + &:last-child { + margin-bottom: 0; + } + } - tr { - display: block; + &.order-review { + .box-items { + .data.table { + thead { + display: block; + + tr { + display: block; + } + + .col.item { + display: block; + padding: 0; + } + } + } } - .col.item { - display: block; - padding: 0; + .data.table { + &:extend(.abs-checkout-order-review all); } - } } - } - - .data.table { - &:extend(.abs-checkout-order-review all); - } - } - .actions-toolbar { - .action { - margin-bottom: @indent__m; - } + .actions-toolbar { + .action { + margin-bottom: @indent__m; + } - > .primary { - margin-bottom: @indent__m; - margin-right: 0; - } + > .primary { + margin-bottom: @indent__m; + margin-right: 0; + } + } } - } } .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__s) { - .multicheckout { - .actions-toolbar { - .column:not(.sidebar-main) & { - &:extend(.abs-reset-left-margin-desktop-s all); - } + .multicheckout { + .actions-toolbar { + .column:not(.sidebar-main) & { + &:extend(.abs-reset-left-margin-desktop-s all); + } - .secondary { - float: none; - margin-top: 11px; - text-align: right; + .secondary { + float: none; + margin-top: 11px; + text-align: right; - .action { - margin-left: @indent__s; + .action { + margin-left: @indent__s; - &.back { - display: block; - float: left; - } + &.back { + display: block; + float: left; + } + } + } } - } - } - - .item-options { - margin: @indent__base 0 0; - } - - .block-content .box { - margin-bottom: 0; - } - .block-shipping { - .box { - &:extend(.abs-add-box-sizing-desktop-s all); - float: left; - width: 25%; - } + .item-options { + margin: @indent__base 0 0; + } - .box-shipping-method { - padding-left: @indent__m; - padding-right: @indent__m; - width: 50%; + .block-content .box { + margin-bottom: 0; + } - .fieldset { - .legend { - &:extend(.abs-reset-left-margin-desktop-s all); - } + .block-shipping { + .box { + &:extend(.abs-add-box-sizing-desktop-s all); + float: left; + width: 25%; + } - .field { - &:before { - display: none; + .box-shipping-method { + padding-left: @indent__m; + padding-right: @indent__m; + width: 50%; + + .fieldset { + .legend { + &:extend(.abs-reset-left-margin-desktop-s all); + } + + .field { + &:before { + display: none; + } + } + } } - } } - } - } - .block-billing { - &:extend(.abs-add-clearfix-desktop-s all); - .box-billing-address { - &:extend(.abs-add-box-sizing-desktop-s all); - float: left; - width: 25%; - } - - .box-billing-method { - &:extend(.abs-add-box-sizing-desktop-s all); - float: left; - padding-left: @indent__m; - width: 50%; - } - } + .block-billing { + &:extend(.abs-add-clearfix-desktop-s all); + .box-billing-address { + &:extend(.abs-add-box-sizing-desktop-s all); + float: left; + width: 25%; + } - &.form.address { - .table-wrapper { - .applicable { - margin: 7px 0 0; + .box-billing-method { + &:extend(.abs-add-box-sizing-desktop-s all); + float: left; + padding-left: @indent__m; + width: 50%; + } } - } - } - &.order-review { - .box-items { - clear: left; - float: none; - padding-top: @indent__xl; - width: auto; - } - - .col.item { - width: 75%; - } - } + &.form.address { + .table-wrapper { + .applicable { + margin: 7px 0 0; + } + } + } - // Payment methods - .methods-payment { - .item-content > .fieldset { - width: auto; + &.order-review { + .box-items { + clear: left; + float: none; + padding-top: @indent__xl; + width: auto; + } - .field { - &.cvv { - display: inline-block; - width: auto; - } + .col.item { + width: 75%; + } } - } - .fieldset > .field:not(.choice) { - > .label { - float: none; - margin-bottom: 8px; - text-align: left; - width: auto; - } + // Payment methods + .methods-payment { + .item-content > .fieldset { + width: auto; + + .field { + &.cvv { + display: inline-block; + width: auto; + } + } + } + + .fieldset > .field:not(.choice) { + > .label { + float: none; + margin-bottom: 8px; + text-align: left; + width: auto; + } - &:not(.cvv) { - .control { - width: 100%; - } + &:not(.cvv) { + .control { + width: 100%; + } + } + } } - } } - } } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { - .multishipping-checkout-success { - .nav-toggle { - display: block; - } + .multishipping-checkout-success { + .nav-toggle { + display: block; + } - .logo { - margin-left: @indent__xl; + .logo { + margin-left: @indent__xl; + } } - } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php index fdc85941b32e9..4261873cc8e6e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product; +use Magento\Framework\Exception\LocalizedException; + /** * @magentoAppArea adminhtml * @magentoDbIsolation enabled @@ -224,6 +226,109 @@ public function testSaveActionCleanAttributeLabelCache() $this->assertEquals('new string translation', $this->_translate('string to translate')); } + /** + * Get attribute data preset. + * + * @return array + */ + private function getLargeOptionsSetAttributeData() + { + return [ + 'frontend_label' => [ + 0 => 'testdrop1', + 1 => '', + 2 => '', + ], + 'frontend_input' => 'select', + 'is_required' => '0', + 'update_product_preview_image' => '0', + 'use_product_image_for_swatch' => '0', + 'visual_swatch_validation' => '', + 'visual_swatch_validation_unique' => '', + 'text_swatch_validation' => '', + 'text_swatch_validation_unique' => '', + 'attribute_code' => 'test_many_options', + 'is_global' => '0', + 'default_value_text' => '', + 'default_value_yesno' => '0', + 'default_value_date' => '', + 'default_value_textarea' => '', + 'is_unique' => '0', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '0', + 'is_comparable' => '0', + 'is_filterable' => '0', + 'is_filterable_in_search' => '0', + 'is_used_for_promo_rules' => '0', + 'is_html_allowed_on_front' => '1', + 'is_visible_on_front' => '0', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '0', + 'swatch_input_type' => 'dropdown', + ]; + } + + /** + * Test attribute saving with large amount of options exceeding maximum allowed by max_input_vars limit. + * @return void + */ + public function testLargeOptionsDataSet() + { + $maxInputVars = ini_get('max_input_vars'); + // Each option is at least 4 variables array (order, admin value, first store view value, delete flag). + // Set options count to exceed max_input_vars by 100 options (400 variables). + $optionsCount = floor($maxInputVars / 4) + 100; + $attributeData = $this->getLargeOptionsSetAttributeData(); + $optionsData = []; + $expectedOptionsLabels = []; + for ($i = 0; $i < $optionsCount; $i++) { + $order = $i + 1; + $expectedOptionLabelOnStoreView = "value_{$i}_store_1"; + $expectedOptionsLabels[$i+1] = $expectedOptionLabelOnStoreView; + $optionsData []= "option[order][option_{$i}]={$order}"; + $optionsData []= "option[value][option_{$i}][0]=value_{$i}_admin"; + $optionsData []= "option[value][option_{$i}][1]={$expectedOptionLabelOnStoreView}"; + $optionsData []= "option[delete][option_{$i}="; + } + $attributeData['serialized_options'] = json_encode($optionsData); + $this->getRequest()->setPostValue($attributeData); + $this->dispatch('backend/catalog/product_attribute/save'); + $entityTypeId = $this->_objectManager->create( + \Magento\Eav\Model\Entity::class + )->setType( + \Magento\Catalog\Model\Product::ENTITY + )->getTypeId(); + + /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ + $attribute = $this->_objectManager->create( + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class + )->setEntityTypeId( + $entityTypeId + ); + try { + $attribute->loadByCode($entityTypeId, 'test_many_options'); + $options = $attribute->getOptions(); + // assert that all options are saved without truncation + $this->assertEquals( + $optionsCount + 1, + count($options), + 'Expected options count does not match (regarding first empty option for non-required attribute)' + ); + + foreach ($expectedOptionsLabels as $optionOrderNum => $label) { + $this->assertEquals( + $label, + $options[$optionOrderNum]->getLabel(), + "Label for option #{$optionOrderNum} does not match expected." + ); + } + } catch (LocalizedException $e) { + $this->fail('Test failed with exception on attribute model load: ' . $e); + } + } + /** * Return translation for a string literal belonging to backend area * diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/AttributeTest.php new file mode 100644 index 0000000000000..969d9530ae542 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/AttributeTest.php @@ -0,0 +1,213 @@ +getRandomColor()}"; + $optionsData []= "optionvisual[value][option_{$i}][0]=value_{$i}_admin"; + $optionsData []= "optionvisual[value][option_{$i}][1]={$expectedOptionLabelOnStoreView}"; + $optionsData []= "optionvisual[delete][option_{$i}]="; + } + $optionsData []= "visual_swatch_validation="; + $optionsData []= "visual_swatch_validation_unique="; + return [ + 'attribute_data' => array_merge_recursive( + [ + 'serialized_swatch_values' => json_encode($optionsData), + ], + $this->getAttributePreset(), + [ + 'frontend_input' => 'swatch_visual' + ] + ), + 'expected_options_count' => $optionsCount + 1, + 'expected_store_labels' => $expectedOptionsLabels + ]; + } + + /** + * Get text swatches data set. + * + * @param int $optionsCount + * @return array + */ + private function getSwatchTextDataSet(int $optionsCount) : array + { + $optionsData = []; + $expectedOptionsLabels = []; + for ($i = 0; $i < $optionsCount; $i++) { + $order = $i + 1; + $expectedOptionLabelOnStoreView = "value_{$i}_store_1"; + $expectedOptionsLabels[$i+1] = $expectedOptionLabelOnStoreView; + $optionsData []= "optiontext[order][option_{$i}]={$order}"; + $optionsData []= "defaulttext[]=option_{$i}"; + $optionsData []= "swatchtext[value][option_{$i}]=x{$i}"; + $optionsData []= "optiontext[value][option_{$i}][0]=value_{$i}_admin"; + $optionsData []= "optiontext[value][option_{$i}][1]={$expectedOptionLabelOnStoreView}"; + $optionsData []= "optiontext[delete][option_{$i}]="; + } + $optionsData []= "text_swatch_validation="; + $optionsData []= "text_swatch_validation_unique="; + return [ + 'attribute_data' => array_merge_recursive( + [ + 'serialized_swatch_values' => json_encode($optionsData), + ], + $this->getAttributePreset(), + [ + 'frontend_input' => 'swatch_text' + ] + ), + 'expected_options_count' => $optionsCount + 1, + 'expected_store_labels' => $expectedOptionsLabels + ]; + } + + /** + * Get data preset for new attribute. + * + * @return array + */ + private function getAttributePreset() : array + { + return [ + 'serialized_options' => '[]', + 'form_key' => 'XxtpPYjm2YPYUlAt', + 'frontend_label' => [ + 0 => 'asdasd', + 1 => '', + 2 => '', + ], + 'is_required' => '0', + 'update_product_preview_image' => '0', + 'use_product_image_for_swatch' => '0', + 'is_global' => '0', + 'default_value_text' => '512', + 'default_value_yesno' => '1', + 'default_value_date' => '1/1/70', + 'default_value_textarea' => '512', + 'is_unique' => '0', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '0', + 'is_comparable' => '0', + 'is_filterable' => '0', + 'is_filterable_in_search' => '0', + 'position' => '0', + 'is_used_for_promo_rules' => '0', + 'is_html_allowed_on_front' => '1', + 'is_visible_on_front' => '0', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '0', + 'attribute_code' => 'test_many_swatches', + ]; + } + + /** + * Data provider for large swatches amount test. + * + * @return array + */ + public function getLargeSwatchesAmountAttributeData() : array + { + $maxInputVars = ini_get('max_input_vars'); + // Each option is at least 7 variables array for a visual swatch. + // Set options count to exceed max_input_vars by 20 options (140 variables). + $swatchVisualOptionsCount = (int)floor($maxInputVars / 7) + 20; + $swatchTextOptionsCount = (int)floor($maxInputVars / 4) + 80; + return [ + 'visual swatches' => $this->getSwatchVisualDataSet($swatchVisualOptionsCount), + 'text swatches' => $this->getSwatchTextDataSet($swatchTextOptionsCount) + ]; + } + + /** + * Test attribute saving with large amount of options exceeding maximum allowed by max_input_vars limit. + * @dataProvider getLargeSwatchesAmountAttributeData() + * @param array $attributeData + * @param int $expectedOptionsCount + * @param array $expectedLabels + * @return void + */ + public function testLargeOptionsDataSet( + array $attributeData, + int $expectedOptionsCount, + array $expectedLabels + ) : void { + $this->getRequest()->setPostValue($attributeData); + $this->dispatch('backend/catalog/product_attribute/save'); + $entityTypeId = $this->_objectManager->create( + \Magento\Eav\Model\Entity::class + )->setType( + \Magento\Catalog\Model\Product::ENTITY + )->getTypeId(); + + /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ + $attribute = $this->_objectManager->create( + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class + )->setEntityTypeId( + $entityTypeId + ); + try { + $attribute->loadByCode($entityTypeId, 'test_many_swatches'); + $options = $attribute->getOptions(); + // assert that all options are saved without truncation + $this->assertEquals( + $expectedOptionsCount, + count($options), + 'Expected options count does not match (regarding first empty option for non-required attribute)' + ); + + foreach ($expectedLabels as $optionOrderNum => $label) { + $this->assertEquals( + $label, + $options[$optionOrderNum]->getLabel(), + "Label for option #{$optionOrderNum} does not match expected." + ); + } + } catch (LocalizedException $e) { + $this->fail('Test failed with exception on attribute model load: ' . $e); + } + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/HhvmCompatibilityTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/HhvmCompatibilityTest.php index e33b771b3c645..c552e0daa97df 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/HhvmCompatibilityTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/HhvmCompatibilityTest.php @@ -47,6 +47,24 @@ class HhvmCompatibilityTest extends \PHPUnit\Framework\TestCase 'serialize_precision', ]; + /** + * Whitelist of variables allowed in files. + * + * @var array + */ + private $whitelistVarsInFiles = [ + 'max_input_vars' => [ + 'integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/AttributeTest.php', + 'integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php', + ] + ]; + + /** + * Test allowed directives. + * + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ public function testAllowedIniGetSetDirectives() { $deniedDirectives = []; @@ -55,7 +73,19 @@ public function testAllowedIniGetSetDirectives() if ($fileDirectives) { $fileDeniedDirectives = array_diff($fileDirectives, $this->allowedDirectives); if ($fileDeniedDirectives) { - $deniedDirectives[$file] = array_unique($fileDeniedDirectives); + $deniedDirectivesInFile = array_unique($fileDeniedDirectives); + foreach ($deniedDirectivesInFile as $key => $deniedDirective) { + if (isset($this->whitelistVarsInFiles[$deniedDirective])) { + foreach ($this->whitelistVarsInFiles[$deniedDirective] as $whitelistFile) { + if (strpos($file, $whitelistFile) !== false) { + unset($deniedDirectivesInFile[$key]); + } + } + } + } + if ($deniedDirectivesInFile) { + $deniedDirectives[$file] = $deniedDirectivesInFile; + } } } } diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Xml/SchemaTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Xml/SchemaTest.php index 8048925a4e542..5877ee5cbcc48 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Xml/SchemaTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Xml/SchemaTest.php @@ -106,7 +106,9 @@ private function _filterSpecialCases(&$files) '#etc/countries.xml$#', '#conf/schema.xml$#', '#layout/swagger_index_index.xml$#', - '#Doc/etc/doc/vars.xml$#' + '#Doc/etc/doc/vars.xml$#', + '#phpunit.xml$#', + '#etc/db_schema.xml$#' ]; foreach ($list as $pattern) { foreach ($files as $key => $value) {