- = /* @escapeNotVerified */ $block->getAddNewAttributeButton(); ?>
+ = /* @escapeNotVerified */ $block->getAddNewAttributeButton('product_form.product_form_data_source'); ?>
@@ -36,3 +36,9 @@
}
}
+
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml
index a93fcf5837519..63a30db901acf 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml
@@ -49,7 +49,8 @@
"= /* @escapeNotVerified */ $block->getComponentName()?>": {
"component": "Magento_ConfigurableProduct/js/variations/steps/summary",
"appendTo": "= /* @escapeNotVerified */ $block->getParentComponentName()?>",
- "variationsComponent": "configurableVariations"
+ "variationsComponent": "configurableVariations",
+ "modalComponent": "product_form.product_form.configurableModal"
}
}
}
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml
index 13b735d7034f4..60fb50cf5ef25 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml
@@ -263,3 +263,9 @@ $currencySymbol = $block->getCurrencySymbol();
}
}
+
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard-ajax.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard-ajax.phtml
new file mode 100644
index 0000000000000..94ebd5cde3db1
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard-ajax.phtml
@@ -0,0 +1,30 @@
+getProductMatrix();
+$attributes = $block->getProductAttributes();
+
+/* @escapeNotVerified */ echo $block->getVariationWizard([
+ 'attributes' => $attributes,
+ 'configurations' => $productMatrix,
+ 'configurableModal' => $block->getConfigurableModal()
+]);
+?>
+
+
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard.phtml
new file mode 100644
index 0000000000000..b18bca75b7d9c
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard.phtml
@@ -0,0 +1,68 @@
+
+getProductMatrix();
+$attributes = $block->getProductAttributes();
+$currencySymbol = $block->getCurrencySymbol();
+?>
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/ui_component/configurable_associated_product_listing.xml b/app/code/Magento/ConfigurableProduct/view/adminhtml/ui_component/configurable_associated_product_listing.xml
index e57b11e84072b..641bf7952389d 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/ui_component/configurable_associated_product_listing.xml
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/ui_component/configurable_associated_product_listing.xml
@@ -71,7 +71,7 @@
-
-
-
-
- configurableProductGrid
+ - product_form.product_form.configurable_associated_product_modal.configurable_associated_product_listing
- selectProduct
-
- ${ $.$data.rowIndex }
@@ -112,7 +112,7 @@
-
+
-
- text
@@ -131,7 +131,7 @@
-
+
-
- textRange
@@ -141,5 +141,48 @@
+
+
+ -
+
- textRange
+ - true
+ - Quantity
+ - 80
+
+
+
+
+
+ -
+
- text
+ - true
+ - Weight
+ - 90
+
+
+
+
+
+ - Magento\Catalog\Model\Product\Attribute\Source\Status
+ -
+
- select
+ - Magento_Ui/js/grid/columns/select
+ - true
+ - select
+ - Status
+ - 100
+
+
+
+
+
+ -
+
- true
+ - false
+ - 99
+ - Attributes
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/ui_component/product_form.xml b/app/code/Magento/ConfigurableProduct/view/adminhtml/ui_component/product_form.xml
new file mode 100644
index 0000000000000..9a44e15b2feb5
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/ui_component/product_form.xml
@@ -0,0 +1,14 @@
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/catalog/product/attribute.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/catalog/product/attribute.js
deleted file mode 100644
index 726cf4ddfbd0c..0000000000000
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/catalog/product/attribute.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-define([
- 'jquery',
- 'jquery/ui',
- 'Magento_Catalog/catalog/product-attributes'
-], function ($) {
- 'use strict';
-
- $.widget('mage.configurableAttribute', $.mage.productAttributes, {
- _prepareUrl: function () {
- var name = $('#configurable-attribute-selector').val();
-
- return this.options.url +
- (/\?/.test(this.options.url) ? '&' : '?') +
- 'set=' + window.encodeURIComponent($('#attribute_set_id').val()) +
- '&attribute[frontend_label]=' +
- window.encodeURIComponent(name);
- }
- });
-
- return $.mage.configurableAttribute;
-});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/advanced-pricing-handler.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/advanced-pricing-handler.js
deleted file mode 100644
index 1f04719da9954..0000000000000
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/advanced-pricing-handler.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-define([
- 'jquery',
- 'Magento_Catalog/catalog/type-events'
-], function ($, productType) {
- 'use strict';
-
- return {
- $initiallyDisabledAttributes: [],
- $links: $('[data-ui-id=product-tabs-tab-link-advanced-pricing]'),
- $tab: $('[data-tab-panel=advanced-pricing]'),
- toggleDisabledAttribute: function (disabled) {
- $('input,select', this.$tab).each(function (index, element) {
- if (!$.inArray(element, this.$initiallyDisabledAttributes)) {
- $(element).attr('disabled', disabled);
- }
- });
- },
- init: function () {
- $(document).on('changeTypeProduct', this._initType.bind(this));
- this._setInitialState();
- this._initType();
- },
- _setInitialState: function () {
- if (this.$initiallyDisabledAttributes.length == 0) {
- this.$initiallyDisabledAttributes = $('input:disabled,select:disabled', this.$tab).toArray();
- }
- },
- _initType: function () {
- var isConfigurable = productType.type.current === 'configurable';
-
- if (isConfigurable) {
- this.$links.hide();
- } else {
- this.$links.show();
- }
-
- this.toggleDisabledAttribute(isConfigurable);
- }
- };
-});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/associated-product-insert-listing.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/associated-product-insert-listing.js
new file mode 100644
index 0000000000000..1d4819546fba3
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/associated-product-insert-listing.js
@@ -0,0 +1,290 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'underscore',
+ 'Magento_Ui/js/form/components/insert-listing'
+], function (_, insertListing) {
+ 'use strict';
+
+ return insertListing.extend({
+ defaults: {
+ gridInitialized: false,
+ paramsUpdated: false,
+ showMassActionColumn: true,
+ currentProductId: 0,
+ dataScopeAssociatedProduct: 'data.associated_product_ids',
+ typeGrid: '',
+ product: {},
+ rowIndexForChange: undefined,
+ changeProductData: [],
+ modules: {
+ productsProvider: '${ $.productsProvider }',
+ productsColumns: '${ $.productsColumns }',
+ productsMassAction: '${ $.productsMassAction }',
+ modalWithGrid: '${ $.modalWithGrid }'
+ },
+ exports: {
+ externalProviderParams: '${ $.externalProvider }:params'
+ },
+ links: {
+ changeProductData: '${ $.provider }:${ $.changeProductProvider }'
+ },
+ listens: {
+ '${ $.externalProvider }:params': '_setFilters _setVisibilityMassActionColumn',
+ '${ $.productsProvider }:data': '_handleManualGridOpening',
+ '${ $.productsMassAction }:selected': '_handleManualGridSelect'
+ }
+ },
+
+ /**
+ * Initialize observables.
+ *
+ * @returns {Object} Chainable.
+ */
+ initObservable: function () {
+ this._super().observe(
+ 'changeProductData'
+ );
+
+ return this;
+ },
+
+ /**
+ * Get ids of used products.
+ *
+ * @returns {Array}
+ */
+ getUsedProductIds: function () {
+ return this.source.get(this.dataScopeAssociatedProduct);
+ },
+
+ /**
+ * Request for render content.
+ *
+ * @returns {Object}
+ */
+ doRender: function (showMassActionColumn, typeGrid) {
+ this.typeGrid = typeGrid;
+ this.showMassActionColumn = showMassActionColumn;
+
+ if (this.gridInitialized) {
+ this.paramsUpdated = false;
+ this._setFilters(this.externalProviderParams);
+ this._setVisibilityMassActionColumn();
+ }
+
+ return this.render();
+ },
+
+ /**
+ * Show grid with assigned product.
+ *
+ * @returns {Object}
+ */
+ showGridAssignProduct: function () {
+ this.product = {};
+ this.rowIndexForChange = undefined;
+
+ return this.doRender(true, 'assignProduct');
+ },
+
+ /**
+ * Show grid with changed product.
+ *
+ * @param {String} rowIndex
+ * @param {String} product
+ */
+ showGridChangeProduct: function (rowIndex, product) {
+ this.rowIndexForChange = rowIndex;
+ this.product = product;
+ this.doRender(false, 'changeProduct');
+ },
+
+ /**
+ * Select product.
+ *
+ * @param {String} rowIndex
+ */
+ selectProduct: function (rowIndex) {
+ this.changeProductData({
+ rowIndex: this.rowIndexForChange,
+ product: this.productsProvider().data.items[rowIndex]
+ });
+ this.modalWithGrid().closeModal();
+ },
+
+ /**
+ * Set visibility state for mass action column
+ *
+ * @private
+ */
+ _setVisibilityMassActionColumn: function () {
+ this.productsMassAction(function (massActionComponent) {
+ this.productsColumns().elems().each(function (rowElement) {
+ rowElement.disableAction = this.showMassActionColumn;
+ }, this);
+ massActionComponent.visible = this.showMassActionColumn;
+ }.bind(this));
+ },
+
+ /**
+ * Set filters.
+ *
+ * @param {Object} params
+ * @private
+ */
+ _setFilters: function (params) {
+ var filterModifier = {},
+ attrCodes,
+ usedProductIds,
+ attributes;
+
+ if (!this.paramsUpdated) {
+ this.gridInitialized = true;
+ this.paramsUpdated = true;
+
+ attrCodes = this._getAttributesCodes(),
+ usedProductIds = this.getUsedProductIds();
+
+ if (this.currentProductId) {
+ usedProductIds.push(this.currentProductId);
+ }
+
+ filterModifier['entity_id'] = {
+ 'condition_type': 'nin', value: usedProductIds
+ };
+ attrCodes.each(function (code) {
+ filterModifier[code] = {
+ 'condition_type': 'notnull'
+ };
+ });
+
+ if (this.typeGrid === 'changeProduct') {
+ attributes = JSON.parse(this.product.attributes);
+
+ filterModifier = _.extend(filterModifier, _.mapObject(attributes, function (value) {
+ return {
+ 'condition_type': 'eq',
+ 'value': value
+ };
+ }));
+
+ params.filters = attributes;
+ }
+
+ params['attributes_codes'] = attrCodes;
+
+ this.set('externalProviderParams', params);
+ this.set('externalFiltersModifier', filterModifier);
+ }
+ },
+
+ /**
+ * Get attribute codes.
+ *
+ * @returns {Array}
+ * @private
+ */
+ _getAttributesCodes: function () {
+ var attrCodes = this.source.get('data.attribute_codes');
+
+ return attrCodes ? attrCodes : [];
+ },
+
+ /**
+ * Get product variations.
+ *
+ * @returns {Array}
+ * @private
+ */
+ _getProductVariations: function () {
+ var matrix = this.source.get('data.configurable-matrix');
+
+ return matrix ? matrix : [];
+ },
+
+ /**
+ * Handle manual grid after opening
+ * @private
+ */
+ _handleManualGridOpening: function (data) {
+ if (data.items.length && this.typeGrid === 'assignProduct') {
+ this.productsColumns().elems().each(function (rowElement) {
+ rowElement.disableAction = true;
+ });
+
+ this._disableRows(data.items);
+ }
+ },
+
+ /**
+ * Handle manual selection.
+ *
+ * @param {Array} selected
+ * @private
+ */
+ _handleManualGridSelect: function (selected) {
+ var selectedRows,
+ selectedVariationKeys;
+
+ if (this.typeGrid === 'assignProduct') {
+ selectedRows = _.filter(this.productsProvider().data.items, function (row) {
+ return selected.indexOf(row['entity_id']) !== -1;
+ });
+ selectedVariationKeys = _.values(this._getVariationKeyMap(selectedRows));
+ this._disableRows(this.productsProvider().data.items, selectedVariationKeys, selected);
+ }
+ },
+
+ /**
+ * Disable rows in grid for products with the same variation key
+ *
+ * @param {Array} items
+ * @param {Array} selectedVariationKeys
+ * @param {Array} selected
+ * @private
+ */
+ _disableRows: function (items, selectedVariationKeys, selected) {
+ selectedVariationKeys = selectedVariationKeys === undefined ? [] : selectedVariationKeys;
+ selected = selected === undefined ? [] : selected;
+ this.productsMassAction(function (massaction) {
+ var configurableVariationKeys = _.union(
+ selectedVariationKeys,
+ _.pluck(this._getProductVariations(), 'variationKey')
+ ),
+ variationKeyMap = this._getVariationKeyMap(items),
+ rowsForDisable = _.keys(_.pick(
+ variationKeyMap,
+ function (variationKey) {
+ return configurableVariationKeys.indexOf(variationKey) !== -1;
+ }
+ ));
+
+ massaction.disabled(_.difference(rowsForDisable, selected));
+ }.bind(this));
+ },
+
+ /**
+ * Get variation key map used in manual grid.
+ *
+ * @param {Array} items
+ * @returns {Array} [{entity_id: variation-key}, ...]
+ * @private
+ */
+ _getVariationKeyMap: function (items) {
+ var variationKeyMap = {};
+
+ _.each(items, function (row) {
+ variationKeyMap[row['entity_id']] = _.values(
+ _.pick(row, this._getAttributesCodes())
+ ).sort().join('-');
+
+ }, this);
+
+ return variationKeyMap;
+ }
+ });
+});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/container-configurable-handler.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/container-configurable-handler.js
new file mode 100644
index 0000000000000..4f109fb978312
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/container-configurable-handler.js
@@ -0,0 +1,69 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'uiComponent'
+], function (Element) {
+ 'use strict';
+
+ return Element.extend({
+ defaults: {
+ listens: {
+ '${ $.provider }:data.is_downloadable': 'handleProductType'
+ },
+ links: {
+ isDownloadable: '${ $.provider }:data.is_downloadable'
+ },
+ modules: {
+ createConfigurableButton: '${$.createConfigurableButton}'
+ }
+ },
+
+ /**
+ * Invokes initialize method of parent class,
+ * contains initialization logic
+ */
+ initialize: function () {
+ this._super();
+ this.handleProductType(this.isDownloadable, true);
+
+ return this;
+ },
+
+ /**
+ * Calls 'initObservable' of parent
+ *
+ * @returns {Object} Chainable.
+ */
+ initObservable: function () {
+ this._super()
+ .observe(['content']);
+
+ return this;
+ },
+
+ /**
+ * Change content for container and visibility for button
+ *
+ * @param {String} isDownloadable
+ * @param {Boolean} onlyContent
+ */
+ handleProductType: function (isDownloadable, onlyContent) {
+ if (isDownloadable === '1') {
+ this.content(this.content2);
+
+ if (onlyContent !== true) {
+ this.createConfigurableButton().visible(false);
+ }
+ } else {
+ this.content(this.content1);
+
+ if (onlyContent !== true) {
+ this.createConfigurableButton().visible(true);
+ }
+ }
+ }
+ });
+});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/custom-options-price-type.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/custom-options-price-type.js
new file mode 100644
index 0000000000000..7a2c19047da59
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/custom-options-price-type.js
@@ -0,0 +1,76 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'underscore',
+ 'Magento_Ui/js/form/element/select'
+], function (_, Select) {
+ 'use strict';
+
+ return Select.extend({
+ defaults: {
+ isConfigurable: false,
+ isFiltered: null,
+ defaultOptions: null,
+ filteredOptions: null,
+ bannedOptions: []
+ },
+
+ /**
+ * Updates options.
+ *
+ * @param {Boolean} variationsEmpty
+ * @returns {Boolean}
+ */
+ updateOptions: function (variationsEmpty) {
+ var isFiltered = this.isConfigurable || !variationsEmpty,
+ value;
+
+ if (this.isFiltered !== isFiltered) {
+ value = this.value();
+
+ this.options(isFiltered ? this.getFilteredOptions() : this.getDefaultOptions());
+ this.value(value);
+ }
+
+ return isFiltered;
+ },
+
+ /**
+ * Get default list of options.
+ *
+ * @returns {Array}
+ */
+ getDefaultOptions: function () {
+ if (this.defaultOptions === null) {
+ this.defaultOptions = this.options();
+ }
+
+ return this.defaultOptions;
+ },
+
+ /**
+ * Get filtered list of options.
+ *
+ * @returns {Array}
+ */
+ getFilteredOptions: function () {
+ var defaultOptions;
+
+ if (this.filteredOptions === null) {
+ defaultOptions = this.getDefaultOptions();
+ this.filteredOptions = [];
+
+ _.each(defaultOptions, function (option) {
+ if (this.bannedOptions.indexOf(option.value) === -1) {
+ this.filteredOptions.push(option);
+ }
+ }, this);
+ }
+
+ return this.filteredOptions;
+ }
+ });
+});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/custom-options-warning.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/custom-options-warning.js
new file mode 100644
index 0000000000000..f6a9a00f2a527
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/custom-options-warning.js
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'Magento_Ui/js/form/components/html'
+], function (Html) {
+ 'use strict';
+
+ return Html.extend({
+ defaults: {
+ isConfigurable: false
+ },
+
+ /**
+ * Updates component visibility state.
+ *
+ * @param {Boolean} variationsEmpty
+ * @returns {Boolean}
+ */
+ updateVisibility: function (variationsEmpty) {
+ var isVisible = this.isConfigurable || !variationsEmpty;
+
+ this.visible(isVisible);
+
+ return isVisible;
+ }
+ });
+});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js
new file mode 100644
index 0000000000000..89f18d6945a02
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js
@@ -0,0 +1,480 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'underscore',
+ 'uiRegistry',
+ 'Magento_Ui/js/dynamic-rows/dynamic-rows'
+], function (_, registry, dynamicRows) {
+ 'use strict';
+
+ return dynamicRows.extend({
+ defaults: {
+ actionsListOpened: false,
+ canEditField: 'canEdit',
+ newProductField: 'newProduct',
+ dataScopeAssociatedProduct: 'data.associated_product_ids',
+ dataProviderFromGrid: '',
+ dataProviderChangeFromGrid: '',
+ insertDataFromGrid: [],
+ changeDataFromGrid: [],
+ dataProviderFromWizard: '',
+ insertDataFromWizard: [],
+ map: null,
+ isEmpty: true,
+ cacheGridData: [],
+ unionInsertData: [],
+ deleteProperty: false,
+ dataLength: 0,
+ identificationProperty: 'id',
+ 'attribute_set_id': '',
+ listens: {
+ 'insertDataFromGrid': 'processingInsertDataFromGrid',
+ 'insertDataFromWizard': 'processingInsertDataFromWizard',
+ 'unionInsertData': 'processingUnionInsertData',
+ 'changeDataFromGrid': 'processingChangeDataFromGrid',
+ 'isEmpty': 'changeVisibility'
+ },
+ imports: {
+ 'attribute_set_id': '${$.provider}:data.product.attribute_set_id'
+ },
+ 'exports': {
+ 'attribute_set_id': '${$.provider}:data.new-variations-attribute-set-id'
+ },
+ modules: {
+ modalWithGrid: '${ $.modalWithGrid }',
+ gridWithProducts: '${ $.gridWithProducts}'
+ }
+ },
+
+ /**
+ * Invokes initialize method of parent class,
+ * contains initialization logic
+ */
+ initialize: function () {
+ this._super()
+ .changeVisibility(this.isEmpty());
+
+ return this;
+ },
+
+ /**
+ * Change visibility
+ *
+ * When isEmpty = true, then visbible = false
+ *
+ * @param {Boolean} isEmpty
+ */
+ changeVisibility: function (isEmpty) {
+ this.visible(!isEmpty);
+ },
+
+ /**
+ * Open modal with grid.
+ *
+ * @param {String} rowIndex
+ */
+ openModalWithGrid: function (rowIndex) {
+ var productSource = this.source.get(this.dataScope + '.' + this.index + '.' + rowIndex),
+ product = {
+ 'id': productSource.id,
+ 'attributes': productSource['configurable_attribute']
+ };
+
+ this.modalWithGrid().openModal();
+ this.gridWithProducts().showGridChangeProduct(rowIndex, product);
+ },
+
+ /**
+ * Initialize children
+ *
+ * @returns {Object} Chainable.
+ */
+ initChildren: function () {
+ var tmpArray = [];
+
+ this.recordData.each(function (recordData) {
+ tmpArray.push(recordData);
+ }, this);
+
+ this.unionInsertData(tmpArray);
+
+ return this;
+ },
+
+ /**
+ * Delete record
+ *
+ * @param {Number} index - row index
+ */
+ deleteRecord: function (index) {
+ var tmpArray;
+
+ this.reRender = false;
+ tmpArray = this.unionInsertData();
+ tmpArray.splice(index, 1);
+
+ if (!tmpArray.length) {
+ this.source.set('data.attributes', []);
+ }
+
+ this.unionInsertData(tmpArray);
+ this.reRender = true;
+ },
+
+ /**
+ * Generate associated products
+ */
+ generateAssociatedProducts: function () {
+ var productsIds = [];
+
+ this.unionInsertData().each(function (data) {
+ if (data.id !== null) {
+ productsIds.push(data.id);
+ }
+ });
+
+ this.source.set(this.dataScopeAssociatedProduct, productsIds);
+ },
+
+ /**
+ * Calls 'initObservable' of parent
+ *
+ * @returns {Object} Chainable.
+ */
+ initObservable: function () {
+ this._super()
+ .observe([
+ 'insertDataFromGrid', 'unionInsertData', 'isEmpty', 'actionsListOpened'
+ ]);
+
+ return this;
+ },
+
+ /**
+ * Process union insert data.
+ *
+ * @param {Array} data
+ */
+ processingUnionInsertData: function (data) {
+ var dataInc = 0,
+ diff = 0,
+ dataCount,
+ elemsCount,
+ lastRecord;
+
+ this.source.remove(this.dataScope + '.' + this.index);
+ this.isEmpty(data.length === 0);
+
+ _.each(data, function (row) {
+ _.each(row, function (value, key) {
+ var path = this.dataScope + '.' + this.index + '.' + dataInc + '.' + key;
+
+ this.source.set(path, value);
+ }, this);
+
+ ++dataInc;
+ }, this);
+
+ // Render
+ dataCount = data.length;
+ elemsCount = this.elems().length;
+
+ if (dataCount > elemsCount) {
+ for (diff = dataCount - elemsCount; diff > 0; diff--) {
+ this.addChild(data, false);
+ }
+ } else {
+ for (diff = elemsCount - dataCount; diff > 0; diff--) {
+ lastRecord =
+ _.findWhere(this.elems(), {
+ index: this.recordIterator - 1
+ }) ||
+ _.findWhere(this.elems(), {
+ index: (this.recordIterator - 1).toString()
+ });
+ lastRecord.destroy();
+ --this.recordIterator;
+ }
+ }
+
+ this.generateAssociatedProducts();
+ },
+
+ /**
+ * Parsed data
+ *
+ * @param {Array} data - array with data
+ * about selected records
+ */
+ processingInsertDataFromGrid: function (data) {
+ var changes,
+ tmpArray;
+
+ if (!data.length) {
+ return;
+ }
+
+ tmpArray = this.unionInsertData();
+
+ changes = this._checkGridData(data);
+ this.cacheGridData = data;
+
+ changes.each(function (changedObject) {
+ var mappedData = this.mappingValue(changedObject);
+
+ mappedData[this.canEditField] = 0;
+ mappedData[this.newProductField] = 0;
+ mappedData.variationKey = this._getVariationKey(changedObject);
+ mappedData['configurable_attribute'] = this._getConfigurableAttribute(changedObject);
+ tmpArray.push(mappedData);
+ }, this);
+
+ this.unionInsertData(tmpArray);
+ },
+
+ /**
+ * Process changes from grid.
+ *
+ * @param {Object} data
+ */
+ processingChangeDataFromGrid: function (data) {
+ var tmpArray = this.unionInsertData(),
+ mappedData = this.mappingValue(data.product);
+
+ mappedData[this.canEditField] = 0;
+ mappedData[this.newProductField] = 0;
+ mappedData.variationKey = this._getVariationKey(data.product);
+ mappedData['configurable_attribute'] = this._getConfigurableAttribute(data.product);
+ tmpArray[data.rowIndex] = mappedData;
+
+ this.unionInsertData(tmpArray);
+ },
+
+ /**
+ * Get variation key.
+ *
+ * @param {Object} data
+ * @returns {String}
+ * @private
+ */
+ _getVariationKey: function (data) {
+ var attrCodes = this.source.get('data.attribute_codes'),
+ key = [];
+
+ attrCodes.each(function (code) {
+ key.push(data[code]);
+ });
+
+ return key.sort().join('-');
+ },
+
+ /**
+ * Get configurable attribute.
+ *
+ * @param {Object} data
+ * @returns {String}
+ * @private
+ */
+ _getConfigurableAttribute: function (data) {
+ var attrCodes = this.source.get('data.attribute_codes'),
+ confAttrs = {};
+
+ attrCodes.each(function (code) {
+ confAttrs[code] = data[code];
+ });
+
+ return JSON.stringify(confAttrs);
+ },
+
+ /**
+ * Process data insertion from wizard
+ *
+ * @param {Object} data
+ */
+ processingInsertDataFromWizard: function (data) {
+ var tmpArray = this.unionInsertData(),
+ productIdsToDelete = this.source.get(this.dataScopeAssociatedProduct),
+ index,
+ product = {};
+
+ tmpArray = this.unsetArrayItem(
+ tmpArray,
+ {
+ id: null
+ }
+ );
+
+ _.each(data, function (row) {
+ var attributesText;
+
+ if (row.productId) {
+ index = _.indexOf(productIdsToDelete, row.productId);
+
+ if (index > -1) {
+ productIdsToDelete.splice(index, 1);
+ tmpArray = this.unsetArrayItem(
+ tmpArray,
+ {
+ id: row.productId
+ }
+ );
+ }
+ }
+
+ attributesText = '';
+ _.each(row.options, function (attribute) {
+ if (attributesText) {
+ attributesText += ', ';
+ }
+ attributesText += attribute['attribute_label'] + ': ' + attribute.label;
+ }, this);
+
+ product = {
+ 'id': row.productId,
+ 'product_link': row.productUrl,
+ 'name': row.name,
+ 'sku': row.sku,
+ 'status': row.status,
+ 'price': row.price,
+ 'price_currency': row.priceCurrency,
+ 'price_string': row.priceCurrency + row.price,
+ 'weight': row.weight,
+ 'qty': row.quantity,
+ 'variationKey': row.variationKey,
+ 'configurable_attribute': row.attribute,
+ 'thumbnail_image': row.images.preview,
+ 'media_gallery': row['media_gallery'],
+ 'swatch_image': row['swatch_image'],
+ 'small_image': row['small_image'],
+ 'thumbnail': row.thumbnail,
+ 'attributes': attributesText
+ };
+ product[this.canEditField] = row.editable;
+ product[this.newProductField] = row.newProduct;
+
+ tmpArray.push(product);
+ }, this);
+
+ _.each(productIdsToDelete, function (id) {
+ tmpArray = this.unsetArrayItem(
+ tmpArray,
+ {
+ id: id
+ }
+ );
+ }, this);
+
+ this.unionInsertData(tmpArray);
+ },
+
+ /**
+ * Remove array items matching condition.
+ *
+ * @param {Array} data
+ * @param {Object} condition
+ * @returns {Array}
+ */
+ unsetArrayItem: function (data, condition) {
+ var objs = _.where(data, condition);
+
+ _.each(objs, function (obj) {
+ var index = _.indexOf(data, obj);
+
+ if (index > -1) {
+ data.splice(index, 1);
+ }
+ });
+
+ return data;
+ },
+
+ /**
+ * Check changed records
+ *
+ * @param {Array} data - array with records data
+ * @returns {Array} Changed records
+ */
+ _checkGridData: function (data) {
+ var cacheLength = this.cacheGridData.length,
+ curData = data.length,
+ max = cacheLength > curData ? this.cacheGridData : data,
+ changes = [],
+ obj = {};
+
+ max.each(function (record, index) {
+ obj[this.map.id] = record[this.map.id];
+
+ if (!_.where(this.cacheGridData, obj).length) {
+ changes.push(data[index]);
+ }
+ }, this);
+
+ return changes;
+ },
+
+ /**
+ * Mapped value
+ */
+ mappingValue: function (data) {
+ var result = {};
+
+ _.each(this.map, function (prop, index) {
+ result[index] = data[prop];
+ });
+
+ return result;
+ },
+
+ /**
+ * Toggle actions list.
+ *
+ * @param {Number} rowIndex
+ * @returns {Object} Chainable.
+ */
+ toggleActionsList: function (rowIndex) {
+ var state = false;
+
+ if (rowIndex !== this.actionsListOpened()) {
+ state = rowIndex;
+ }
+ this.actionsListOpened(state);
+
+ return this;
+ },
+
+ /**
+ * Close action list.
+ *
+ * @param {Number} rowIndex
+ * @returns {Object} Chainable
+ */
+ closeList: function (rowIndex) {
+ if (this.actionsListOpened() === rowIndex) {
+ this.actionsListOpened(false);
+ }
+
+ return this;
+ },
+
+ /**
+ * Toggle product status.
+ *
+ * @param {Number} rowIndex
+ */
+ toggleStatusProduct: function (rowIndex) {
+ var tmpArray = this.unionInsertData(),
+ status = parseInt(tmpArray[rowIndex].status, 10);
+
+ if (status === 1) {
+ tmpArray[rowIndex].status = 2;
+ } else {
+ tmpArray[rowIndex].status = 1;
+ }
+
+ this.unionInsertData(tmpArray);
+ }
+ });
+});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/file-uploader.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/file-uploader.js
new file mode 100644
index 0000000000000..faeb48f3893d9
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/file-uploader.js
@@ -0,0 +1,82 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+define([
+ 'Magento_Ui/js/form/element/file-uploader'
+], function (Element) {
+ 'use strict';
+
+ return Element.extend({
+ processedFile: {},
+ actionsListOpened: false,
+ defaults: {
+ fileInputName: ''
+ },
+
+ /**
+ * Initialize observables.
+ *
+ * @returns {Object} Chainable.
+ */
+ initObservable: function () {
+ this._super().observe(['processedFile', 'actionsListOpened']);
+
+ return this;
+ },
+
+ /**
+ * Adds provided file to the files list.
+ *
+ * @param {Object} file
+ * @returns {Object} Chainable.
+ */
+ addFile: function (file) {
+ this.processedFile(this.processFile(file));
+
+ this.value(this.processedFile().file);
+
+ return this;
+ },
+
+ /**
+ * Toggle actions list.
+ *
+ * @returns {Object} Chainable.
+ */
+ toggleActionsList: function () {
+ if (this.actionsListOpened()) {
+ this.actionsListOpened(false);
+ } else {
+ this.actionsListOpened(true);
+ }
+
+ return this;
+ },
+
+ /**
+ * Close action list.
+ *
+ * @returns {Object} Chainable
+ */
+ closeList: function () {
+ if (this.actionsListOpened()) {
+ this.actionsListOpened(false);
+ }
+
+ return this;
+ },
+
+ /**
+ * Delete Image
+ *
+ * @returns {Object} Chainable
+ */
+ deleteImage: function () {
+ this.processedFile({});
+ this.value(null);
+
+ return this;
+ }
+ });
+});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/modal-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/modal-configurable.js
new file mode 100644
index 0000000000000..7ba004072c26e
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/modal-configurable.js
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'Magento_Ui/js/modal/modal-component'
+], function (Modal) {
+ 'use strict';
+
+ return Modal.extend({
+ defaults: {
+ modules: {
+ form: '${ $.formName }',
+ targetWizard: '${ $.wizardName }'
+ }
+ },
+
+ /**
+ * Open modal
+ */
+ openModal: function () {
+ this.form().validate();
+
+ if (this.form().source.get('params.invalid') === false) {
+ this._super();
+ }
+ }
+ });
+});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/configurable-type-handler.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/configurable-type-handler.js
index 3a77ac24ba6f9..fc61e624f7f4e 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/configurable-type-handler.js
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/configurable-type-handler.js
@@ -5,13 +5,11 @@
define([
'jquery',
'Magento_Catalog/catalog/type-events',
- 'Magento_ConfigurableProduct/js/advanced-pricing-handler',
- 'Magento_ConfigurableProduct/js/options/price-type-handler',
'collapsible',
'Magento_Ui/js/modal/modal',
'mage/translate',
'domReady!'
-], function ($, productType, advancedPricingHandler, priceTypeHandler) {
+], function ($, productType) {
'use strict';
return {
@@ -84,7 +82,9 @@ define([
* @private
*/
_initType: function () {
- var suggestContainer = $('#product-template-suggest-container .action-dropdown > .action-toggle');
+
+ /*var suggestContainer = $('#product-template-suggest-container .action-dropdown > .action-toggle');
+
if (productType.type.current === 'configurable') {
this._setElementDisabled(suggestContainer.addClass('disabled'), true);
@@ -98,12 +98,15 @@ define([
this._setElementDisabled($('#inventory_stock_availability'), true);
this._setElementDisabled($('#qty'), false, true);
}
+ */
- if (['simple', 'virtual', 'configurable'].indexOf(productType.type.current) < 0) {
+ /*if (['simple', 'virtual', 'configurable'].indexOf(productType.type.current) < 0) {
this.hide();
} else {
this.show();
- }
+ }*/
+
+ this.show();
},
/**
@@ -114,12 +117,13 @@ define([
this.$block = $(data.blockId + ' input[name="attributes[]"]');
this.hasVariations = data.hasVariations;
- advancedPricingHandler.init();
- priceTypeHandler.init();
+ //advancedPricingHandler.init();
+ //priceTypeHandler.init();
- if (productType.type.init === 'configurable' && !this.hasVariations) {
+ /*if (productType.type.init === 'configurable' && !this.hasVariations) {
$(document).trigger('setTypeProduct', 'simple');
- }
+ }*/
+ $(document).trigger('setTypeProduct', 'simple');
this.bindAll();
this._initType();
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/options/price-type-handler.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/options/price-type-handler.js
index d0d0314443b5d..d486723fc46d0 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/options/price-type-handler.js
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/options/price-type-handler.js
@@ -2,7 +2,7 @@
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-
+/*
define([
'jquery',
'Magento_Catalog/catalog/type-events',
@@ -80,3 +80,4 @@ define([
}
};
});
+*/
\ No newline at end of file
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/summary.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/summary.js
index 0a1088393c04f..6955bb511f606 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/summary.js
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/summary.js
@@ -15,7 +15,8 @@ define([
return Component.extend({
defaults: {
modules: {
- variationsComponent: '${ $.variationsComponent }'
+ variationsComponent: '${ $.variationsComponent }',
+ modalComponent: '${ $.modalComponent }'
},
notificationMessage: {
text: null,
@@ -41,7 +42,7 @@ define([
variations: [],
generateGrid: function (variations, getSectionValue) {
var productSku = this.variationsComponent().getProductValue('sku'),
- productPrice = this.variationsComponent().getProductValue('price'),
+ productPrice = this.variationsComponent().getProductPrice(),
productWeight = this.variationsComponent().getProductValue('weight'),
variationsKeys = [],
gridExisting = [],
@@ -159,7 +160,7 @@ define([
},
force: function () {
this.variationsComponent().render(this.variations, this.attributes());
- $('[data-role=step-wizard-dialog]').trigger('closeModal');
+ this.modalComponent().closeModal();
},
back: function () {
}
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js
index 878b0459fb0e9..f88d60b5fe9f4 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js
@@ -9,8 +9,10 @@ define([
'jquery',
'ko',
'underscore',
- 'Magento_Ui/js/modal/alert'
-], function (Component, $, ko, _, alert) {
+ 'Magento_Ui/js/modal/alert',
+ 'uiRegistry',
+ 'mage/translate'
+], function (Component, $, ko, _, alert, registry, $t) {
'use strict';
function UserException(message) {
@@ -23,77 +25,53 @@ define([
defaults: {
opened: false,
attributes: [],
+ usedAttributes: [],
+ attributeCodes: [],
+ attributesData: {},
productMatrix: [],
variations: [],
+ formSaveParams: [],
productAttributes: [],
+ disabledAttributes: [],
fullAttributes: [],
rowIndexToEdit: false,
productAttributesMap: null,
+ value: [],
modules: {
- associatedProductGrid: '${ $.configurableProductGrid }'
+ associatedProductGrid: '${ $.configurableProductGrid }',
+ wizardButtonElement: '${ $.wizardModalButtonName }',
+ formElement: '${ $.formName }',
+ attributeSetHandlerModal: '${ $.attributeSetHandler }',
+ messageBoxElement: 'ns = ${ $.ns }, index = affectedAttributeSetError'
+ },
+ imports: {
+ attributeSetName: '${ $.provider }:configurableNewAttributeSetName',
+ attributeSetId: '${ $.provider }:configurableExistingAttributeSetId',
+ attributeSetSelection: '${ $.provider }:configurableAffectedAttributeSet',
+ productPrice: '${ $.provider }:data.product.price'
+ },
+ links: {
+ value: '${ $.provider }:${ $.dataScopeVariations }',
+ usedAttributes: '${ $.provider }:${ $.dataScopeAttributes }',
+ attributesData: '${ $.provider }:${ $.dataScopeAttributesData }',
+ attributeCodes: '${ $.provider }:${ $.dataScopeAttributeCodes }',
+ skeletonAttributeSet: '${ $.provider }:data.new-variations-attribute-set-id'
}
},
initialize: function () {
this._super();
- if (this.variations.length) {
- this.render(this.variations, this.productAttributes);
- }
+ this.changeButtonWizard();
this.initProductAttributesMap();
+ this.disableConfigurableAttributes(this.productAttributes);
},
initObservable: function () {
- this._super().observe('actions opened attributes productMatrix');
+ this._super().observe(
+ 'actions opened attributes productMatrix value usedAttributes attributesData attributeCodes'
+ );
return this;
},
- showGrid: function (rowIndex) {
- var product = this.productMatrix()[rowIndex],
- attributes = JSON.parse(product.attribute),
- filterModifier = product.productId ? {
- 'entity_id': {
- 'condition_type': 'neq', value: product.productId
- }
- } : {};
- this.rowIndexToEdit = rowIndex;
-
- filterModifier = _.extend(filterModifier, _.mapObject(attributes, function (value) {
- return {
- 'condition_type': 'eq',
- 'value': value
- };
- }));
- this.associatedProductGrid().open(
- {
- 'filters': attributes,
- 'filters_modifier': filterModifier
- },
- 'changeProduct',
- false
- );
- },
- changeProduct: function (newProducts) {
- var oldProduct = this.productMatrix()[this.rowIndexToEdit],
- newProduct = this._makeProduct(_.extend(oldProduct, newProducts[0]));
- this.productAttributesMap[this.getVariationKey(newProduct.options)] = newProduct.productId;
- this.productMatrix.splice(this.rowIndexToEdit, 1, newProduct);
- },
- appendProducts: function (newProducts) {
- this.productMatrix.push.apply(
- this.productMatrix,
- _.map(
- newProducts,
- _.wrap(
- this._makeProduct.bind(this),
- function (func, product) {
- var newProduct = func(product);
- this.productAttributesMap[this.getVariationKey(newProduct.options)] = newProduct.productId;
-
- return newProduct;
- }.bind(this)
- )
- )
- );
- },
_makeProduct: function (product) {
var productId = product['entity_id'] || product.productId || null,
attributes = _.pick(product, this.attributes.pluck('code')),
@@ -155,25 +133,95 @@ define([
return result;
},
- getAttributeRowName: function (attribute, field) {
- return 'product[configurable_attributes_data][' + attribute.id + '][' + field + ']';
- },
- getOptionRowName: function (attribute, option, field) {
- return 'product[configurable_attributes_data][' + attribute.id + '][values][' +
- option.value + '][' + field + ']';
- },
render: function (variations, attributes) {
this.changeButtonWizard();
this.populateVariationMatrix(variations);
this.attributes(attributes);
- this.initImageUpload();
this.disableConfigurableAttributes(attributes);
- this.showPrice();
+ this.handleValue(variations);
+ this.handleAttributes();
},
changeButtonWizard: function () {
- var $button = $('[data-action=open-steps-wizard] [data-role=button-label]');
- $button.text($button.attr('data-edit-label'));
+ if (this.variations.length) {
+ this.wizardButtonElement().title(this.wizardModalButtonTitle);
+ }
},
+ handleValue: function (variations) {
+ var tmpArray = [];
+
+
+ _.each(variations, function (variation) {
+ var attributes = _.reduce(variation.options, function (memo, option) {
+ var attribute = {};
+ attribute[option['attribute_code']] = option.value;
+
+ return _.extend(memo, attribute);
+ }, {});
+ var gallery = {images: {}};
+ var defaultImage = null;
+
+ _.each(variation.images.images, function (image) {
+ gallery.images[image.file_id] = {
+ position: image.position,
+ file: image.file,
+ disabled: image.disabled,
+ label: ''
+ };
+ if (image.position == 1) {
+ defaultImage = image.file;
+ }
+ }, this);
+
+ tmpArray.push(_.extend(variation, {
+ productId: variation.productId || null,
+ name: variation.name || variation.sku,
+ priceCurrency: this.currencySymbol,
+ weight: variation.weight,
+ attribute: JSON.stringify(attributes),
+ variationKey: this.getVariationKey(variation.options),
+ editable: variation.editable === undefined ? 0 : 1,
+ productUrl: this.buildProductUrl(variation.productId),
+ status: variation.status === undefined ? 1 : parseInt(variation.status, 10),
+ newProduct: variation.productId ? 0 : 1,
+ media_gallery: gallery,
+ swatch_image: defaultImage,
+ small_image: defaultImage,
+ thumbnail: defaultImage,
+ image: defaultImage
+ }));
+ }, this);
+
+ this.value(tmpArray);
+ },
+ handleAttributes: function () {
+ var tmpArray = [], codesArray = [], tmpOptions = {}, option = {}, position = 0, values = {};
+
+ _.each(this.attributes(), function (attribute) {
+ tmpArray.push(attribute.id);
+ codesArray.push(attribute.code);
+ values = {};
+ _.each(attribute.chosen, function (row) {
+ values[row.value] = {
+ "include": "1",
+ "value_index": row.value
+ };
+ }, this);
+ option = {
+ "attribute_id": attribute.id,
+ "code": attribute.code,
+ "label": attribute.label,
+ "position": position,
+ "values": values
+ };
+ tmpOptions[attribute.id] = option;
+ position++;
+ }, this);
+
+ this.attributesData(tmpOptions);
+ this.usedAttributes(tmpArray);
+ this.attributeCodes(codesArray);
+ },
+
/**
* Get attributes options
@@ -193,6 +241,7 @@ define([
_.each(variations, function (variation) {
var attributes = _.reduce(variation.options, function (memo, option) {
var attribute = {};
+
attribute[option['attribute_code']] = option.value;
return _.extend(memo, attribute);
@@ -212,52 +261,6 @@ define([
buildProductUrl: function (productId) {
return this.productUrl.replace('%id%', productId);
},
- removeProduct: function (rowIndex) {
- this.opened(false);
- var removedProduct = this.productMatrix.splice(rowIndex, 1);
- delete this.productAttributesMap[this.getVariationKey(removedProduct[0].options)];
-
- if (this.productMatrix().length === 0) {
- this.attributes.each(function (attribute) {
- $('[data-attribute-code="' + attribute.code + '"] select').removeProp('disabled');
- });
- }
- this.showPrice();
- },
- toggleProduct: function (rowIndex) {
- var product, row, productChanged = {};
-
- if (this.productMatrix()[rowIndex].editable) {
- row = $('[data-row-number=' + rowIndex + ']');
- _.each(['name','sku','qty','weight','price'], function (column) {
- productChanged[column] = $(
- 'input[type=text]',
- row.find($('[data-column="%s"]'.replace('%s', column)))
- ).val();
- });
- }
- product = this.productMatrix.splice(rowIndex, 1)[0];
- product = _.extend(product, productChanged);
- product.status = +!product.status;
- this.productMatrix.splice(rowIndex, 0, product);
- },
- toggleList: function (rowIndex) {
- var state = false;
-
- if (rowIndex !== this.opened()) {
- state = rowIndex;
- }
- this.opened(state);
-
- return this;
- },
- closeList: function (rowIndex) {
- if (this.opened() === rowIndex()) {
- this.opened(false);
- }
-
- return this;
- },
getVariationKey: function (options) {
return _.pluck(options, 'value').sort().join('-');
},
@@ -272,131 +275,156 @@ define([
}.bind(this));
}
},
+ disableConfigurableAttributes: function (attributes) {
+ var element;
+
+ _.each(this.disabledAttributes, function (attribute) {
+ registry.get('index = ' + attribute).disabled(false);
+ });
+ this.disabledAttributes = [];
+
+ _.each(attributes, function (attribute) {
+ element = registry.get('index = ' + attribute.code);
+ if (!_.isUndefined(element)) {
+ element.disabled(true);
+ this.disabledAttributes.push(attribute.code);
+ }
+ }, this);
+ },
/**
- * Is show preview image
- * @see use in matrix.phtml
- * @function
- * @event
- * @param {object} variation
- * @returns {*|boolean}
+ * Get currency symbol
+ * @returns {*}
*/
- isShowPreviewImage: function (variation) {
- return variation.images.preview && (!variation.editable || variation.images.file);
+ getCurrencySymbol: function () {
+ return this.currencySymbol;
},
- generateImageGallery: function (variation) {
- var gallery = [],
- imageFields = ['position', 'file', 'disabled', 'label'];
- _.each(variation.images.images, function (image) {
- _.each(imageFields, function (field) {
- gallery.push(
- ' '
- );
- }, this);
- _.each(image.galleryTypes, function (imageType) {
- gallery.push(
- ' '
- );
- }, this);
+
+ /**
+ * Chose action for the form save button
+ */
+ saveFormHandler: function() {
+ if (this.checkForNewAttributes()) {
+ this.formSaveParams = arguments;
+ this.attributeSetHandlerModal().openModal();
+ } else {
+ this.formElement().save(arguments[0], arguments[1]);
+ }
+ },
+
+ /**
+ * Check for newly added attributes
+ * @returns {Boolean}
+ */
+ checkForNewAttributes: function () {
+ var element, newAttributes = false;
+
+ _.each(this.source.get('data.attribute_codes'), function (attribute) {
+ element = registry.get('index = ' + attribute);
+
+ if (_.isUndefined(element)) {
+ newAttributes = true;
+ }
}, this);
- return gallery.join('\n');
+ return newAttributes;
+ },
+
+ /**
+ * New attributes handler
+ * @returns {Boolean}
+ */
+ addNewAttributeSetHandler: function() {
+ this.formElement().validate();
+
+ if (this.formElement().source.get('params.invalid') === false) {
+ var choosenAttributeSetOption = this.attributeSetSelection;
+
+ if (choosenAttributeSetOption === 'new') {
+ this.createNewAttributeSet();
+ return false;
+ }
+
+ if (choosenAttributeSetOption === 'existing') {
+ this.set(
+ 'skeletonAttributeSet',
+ this.attributeSetId
+ );
+ }
+
+ this.closeDialogAndProcessForm();
+ return true;
+ }
},
- initImageUpload: function () {
- require([
- 'mage/template',
- 'jquery/file-uploader',
- 'mage/mage',
- 'mage/translate',
- 'domReady!'
- ], function (mageTemplate) {
-
- var matrix = $('[data-role=product-variations-matrix]');
- matrix.find('[data-action=upload-image]').find('[name=image]').each(function () {
- var imageColumn = $(this).closest('[data-column=image]');
-
- if (imageColumn.find('[data-role=image]').length) {
- imageColumn.find('[data-toggle=dropdown]').dropdown().show();
+
+ /**
+ * Handles new attribute set creation
+ * @returns {Boolean}
+ */
+ createNewAttributeSet: function() {
+ var ns = this.formElement().ns,
+ messageBoxElement = registry.get('index = ' + ns + '.affectedAttributeSetError');
+
+ messageBoxElement.visible(false);
+
+ $.ajax({
+ type: 'POST',
+ url: this.attributeSetCreationUrl,
+ data: {
+ gotoEdit: 1,
+ attribute_set_name: this.attributeSetName,
+ skeleton_set: this.skeletonAttributeSet,
+ return_session_messages_only: 1
+ },
+ dataType: 'json',
+ showLoader: true,
+ context: this
+ })
+
+ .success(function (data) {
+ if (!data.error) {
+ this.set(
+ 'skeletonAttributeSet',
+ data.id
+ );
+ messageBoxElement.content(data.messages);
+ messageBoxElement.visible(true);
+ this.closeDialogAndProcessForm();
+ } else {
+ messageBoxElement.content(data.messages);
+ messageBoxElement.visible(true);
+ }
+
+ return false;
+ })
+
+ .error(function (xhr) {
+ if (xhr.statusText === 'abort') {
+ return;
}
- $(this).fileupload({
- dataType: 'json',
- dropZone: $(this).closest('[data-role=row]'),
- acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
- done: function (event, data) {
- var tmpl, parentElement, uploaderControl, imageElement;
-
- if (!data.result) {
- return;
- }
-
- if (!data.result.error) {
- parentElement = $(event.target).closest('[data-column=image]');
- uploaderControl = parentElement.find('[data-action=upload-image]');
- imageElement = parentElement.find('[data-role=image]');
-
- if (imageElement.length) {
- imageElement.attr('src', data.result.url);
- } else {
- tmpl = mageTemplate(matrix.find('[data-template-for=variation-image]').html());
-
- $(tmpl({
- data: data.result
- })).prependTo(uploaderControl);
- }
- parentElement.find('[name$="[image]"]').val(data.result.file);
- parentElement.find('[data-toggle=dropdown]').dropdown().show();
- } else {
- alert({
- content: $.mage.__('We don\'t recognize or support this file extension type.')
- });
- }
- },
- start: function (event) {
- $(event.target).closest('[data-action=upload-image]').addClass('loading');
- },
- stop: function (event) {
- $(event.target).closest('[data-action=upload-image]').removeClass('loading');
- }
+
+ alert({
+ content: $t('Something went wrong.')
});
});
- matrix.find('[data-action=no-image]').click(function (event) {
- var parentElement = $(event.target).closest('[data-column=image]');
- parentElement.find('[data-role=image]').remove();
- parentElement.find('[name$="[image]"]').val('');
- parentElement.find('[data-toggle=dropdown]').trigger('close.dropdown').hide();
- });
- });
- },
- disableConfigurableAttributes: function (attributes) {
- $('[data-attribute-code] select.disabled-configurable-elements')
- .removeClass('disabled-configurable-elements')
- .prop('disabled', false);
- _.each(attributes, function (attribute) {
- $('[data-attribute-code="' + attribute.code + '"] select')
- .addClass('disabled-configurable-elements')
- .prop('disabled', true);
- });
+
+ return false;
},
- showPrice: function () {
- var priceContainer = $('[id="attribute-price-container"]');
- if (this.productMatrix().length !== 0) {
- priceContainer.hide();
- priceContainer.find('input').prop('disabled', true);
- } else {
- priceContainer.show();
- priceContainer.find('input').prop('disabled', false);
- }
+
+ /**
+ * Closes attribute set handler modal and process product form
+ */
+ closeDialogAndProcessForm: function() {
+ this.attributeSetHandlerModal().closeModal();
+ this.formElement().save(this.formSaveParams[0], this.formSaveParams[1]);
},
/**
- * Get currency symbol
+ * Retrieves product price
* @returns {*}
*/
- getCurrencySymbol: function () {
- return this.currencySymbol;
+ getProductPrice: function() {
+ return this.productPrice;
}
});
});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/actions-list.html b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/actions-list.html
new file mode 100644
index 0000000000000..934acac25f1c4
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/actions-list.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/cell-html.html b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/cell-html.html
new file mode 100644
index 0000000000000..541836b7b74c5
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/cell-html.html
@@ -0,0 +1,13 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/cell-status.html b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/cell-status.html
new file mode 100644
index 0000000000000..1011fe6f31fde
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/cell-status.html
@@ -0,0 +1,13 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/file-uploader.html b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/file-uploader.html
new file mode 100644
index 0000000000000..ec4669ed843c1
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/components/file-uploader.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Downloadable/view/adminhtml/web/js/components/is-downloadable-handler.js b/app/code/Magento/Downloadable/view/adminhtml/web/js/components/is-downloadable-handler.js
index f372cda0405ce..9003055c8b459 100644
--- a/app/code/Magento/Downloadable/view/adminhtml/web/js/components/is-downloadable-handler.js
+++ b/app/code/Magento/Downloadable/view/adminhtml/web/js/components/is-downloadable-handler.js
@@ -10,8 +10,7 @@ define([
return Element.extend({
defaults: {
listens: {
- disabled: 'changeVisibility',
- checked: 'changeVisibility'
+ disabled: 'changeVisibility'
},
modules: {
samplesFieldset: '${ $.samplesFieldset }',
@@ -32,6 +31,16 @@ define([
this.linksFieldset().visible(false);
}
}
+ },
+
+ /**
+ * Handle checked state changes for checkbox / radio button.
+ *
+ * @param {Boolean} newChecked
+ */
+ onCheckedChanged: function (newChecked) {
+ this.changeVisibility();
+ this._super(newChecked);
}
});
});
diff --git a/app/code/Magento/Eav/Api/Data/AttributeGroupInterface.php b/app/code/Magento/Eav/Api/Data/AttributeGroupInterface.php
index 9c02bda805670..1bc89df8d03d0 100644
--- a/app/code/Magento/Eav/Api/Data/AttributeGroupInterface.php
+++ b/app/code/Magento/Eav/Api/Data/AttributeGroupInterface.php
@@ -10,10 +10,12 @@
interface AttributeGroupInterface extends ExtensibleDataInterface
{
const GROUP_ID = 'attribute_group_id';
-
const GROUP_NAME = 'attribute_group_name';
-
const ATTRIBUTE_SET_ID = 'attribute_set_id';
+ const SORT_ORDER = 'sort_order';
+ const DEFAULT_ID = 'default_id';
+ const ATTRIBUTE_GROUP_CODE = 'attribute_group_code';
+ const SCOPE_CODE = 'tab_group_code';
/**
* Retrieve id
@@ -60,6 +62,66 @@ public function getAttributeSetId();
*/
public function setAttributeSetId($attributeSetId);
+ /**
+ * Retrieve sort order
+ *
+ * @return int
+ */
+ public function getSortOrder();
+
+ /**
+ * Set sort order
+ *
+ * @param int $sortOrder
+ * @return $this
+ */
+ public function setSortOrder($sortOrder);
+
+ /**
+ * Retrieve default ID
+ *
+ * @return int
+ */
+ public function getDefaultId();
+
+ /**
+ * Set default ID
+ *
+ * @param int $defaultId
+ * @return $this
+ */
+ public function setDefaultId($defaultId);
+
+ /**
+ * Retrieve attribute group code
+ *
+ * @return string
+ */
+ public function getAttributeGroupCode();
+
+ /**
+ * Set attribute group code
+ *
+ * @param string $attributeGroupCode
+ * @return $this
+ */
+ public function setAttributeGroupCode($attributeGroupCode);
+
+ /**
+ * Retrieve scope code
+ *
+ * @return string
+ */
+ public function getScopeCode();
+
+ /**
+ * Set scope code
+ *
+ * @param string $scopeCode
+ * @return $this
+ */
+ public function setScopeCode($scopeCode);
+
/**
* Retrieve existing extension attributes object.
*
diff --git a/app/code/Magento/Eav/Model/Adminhtml/Attribute/Validation/Rules/Options.php b/app/code/Magento/Eav/Model/Adminhtml/Attribute/Validation/Rules/Options.php
new file mode 100644
index 0000000000000..737cbea0f9b83
--- /dev/null
+++ b/app/code/Magento/Eav/Model/Adminhtml/Attribute/Validation/Rules/Options.php
@@ -0,0 +1,30 @@
+ '', 'label' => ''), ...)
+ */
+ public function toOptionArray()
+ {
+ return [
+ ['value' => '', 'label' => __('None')],
+ ['value' => 'validate-number', 'label' => __('Decimal Number')],
+ ['value' => 'validate-digits', 'label' => __('Integer Number')],
+ ['value' => 'validate-email', 'label' => __('Email')],
+ ['value' => 'validate-url', 'label' => __('URL')],
+ ['value' => 'validate-alpha', 'label' => __('Letters')],
+ ['value' => 'validate-alphanum', 'label' => __('Letters (a-z, A-Z) or Numbers (0-9)')]
+ ];
+ }
+}
diff --git a/app/code/Magento/Eav/Model/Attribute/GroupRepository.php b/app/code/Magento/Eav/Model/Attribute/GroupRepository.php
index d5bebb357123c..caa85a5d66d3d 100644
--- a/app/code/Magento/Eav/Model/Attribute/GroupRepository.php
+++ b/app/code/Magento/Eav/Model/Attribute/GroupRepository.php
@@ -121,6 +121,10 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr
$collection->setAttributeSetFilter($attributeSetId);
$collection->setSortOrder();
+ if ($attributeGroupCode = $this->retrieveAttributeGroupCodeFromSearchCriteria($searchCriteria)) {
+ $collection->addFilter('attribute_group_code', $attributeGroupCode);
+ }
+
$searchResult = $this->searchResultsFactory->create();
$searchResult->setSearchCriteria($searchCriteria);
$searchResult->setItems($collection->getItems());
@@ -188,4 +192,23 @@ protected function retrieveAttributeSetIdFromSearchCriteria(
}
return null;
}
+
+ /**
+ * Retrieve attribute group code
+ *
+ * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
+ * @return null|string
+ */
+ protected function retrieveAttributeGroupCodeFromSearchCriteria(
+ \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
+ ) {
+ foreach ($searchCriteria->getFilterGroups() as $group) {
+ foreach ($group->getFilters() as $filter) {
+ if ($filter->getField() == 'attribute_group_code') {
+ return $filter->getValue();
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/app/code/Magento/Eav/Model/AttributeProvider.php b/app/code/Magento/Eav/Model/AttributeProvider.php
index 56eb5b46a5ba8..1bb21fae46d14 100644
--- a/app/code/Magento/Eav/Model/AttributeProvider.php
+++ b/app/code/Magento/Eav/Model/AttributeProvider.php
@@ -60,7 +60,7 @@ public function getAttributes($entityType)
$metadata = $this->metadataPool->getMetadata($entityType);
$searchResult = $this->attributeRepository->getList(
$metadata->getEavEntityType(),
- $this->searchCriteriaBuilder->create()
+ $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create()
);
$attributes = [];
foreach ($searchResult->getItems() as $attribute) {
diff --git a/app/code/Magento/Eav/Model/AttributeRepository.php b/app/code/Magento/Eav/Model/AttributeRepository.php
index 6545375a245cf..9c23d9b276d3f 100644
--- a/app/code/Magento/Eav/Model/AttributeRepository.php
+++ b/app/code/Magento/Eav/Model/AttributeRepository.php
@@ -103,7 +103,7 @@ public function getList($entityTypeCode, \Magento\Framework\Api\SearchCriteriaIn
'main_table.entity_type_id = entity_type.entity_type_id',
[]
);
- $attributeCollection->join(
+ $attributeCollection->joinLeft(
['eav_entity_attribute' => $attributeCollection->getTable('eav_entity_attribute')],
'main_table.attribute_id = eav_entity_attribute.attribute_id',
[]
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Group.php b/app/code/Magento/Eav/Model/Entity/Attribute/Group.php
index 3ade99cd99a7e..bdb2a6f3a1dc2 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/Group.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/Group.php
@@ -13,14 +13,6 @@
*
* @method \Magento\Eav\Model\ResourceModel\Entity\Attribute\Group _getResource()
* @method \Magento\Eav\Model\ResourceModel\Entity\Attribute\Group getResource()
- * @method int getSortOrder()
- * @method \Magento\Eav\Model\Entity\Attribute\Group setSortOrder(int $value)
- * @method int getDefaultId()
- * @method \Magento\Eav\Model\Entity\Attribute\Group setDefaultId(int $value)
- * @method string getAttributeGroupCode()
- * @method \Magento\Eav\Model\Entity\Attribute\Group setAttributeGroupCode(string $value)
- * @method string getTabGroupCode()
- * @method \Magento\Eav\Model\Entity\Attribute\Group setTabGroupCode(string $value)
*/
class Group extends \Magento\Framework\Model\AbstractExtensibleModel implements
\Magento\Eav\Api\Data\AttributeGroupInterface
@@ -172,6 +164,70 @@ public function setAttributeSetId($attributeSetId)
return $this->setData(self::ATTRIBUTE_SET_ID, $attributeSetId);
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getSortOrder()
+ {
+ return $this->getData(self::SORT_ORDER);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setSortOrder($sortOrder)
+ {
+ return $this->setData(self::SORT_ORDER, $sortOrder);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDefaultId()
+ {
+ return $this->getData(self::DEFAULT_ID);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setDefaultId($defaultId)
+ {
+ return $this->setData(self::DEFAULT_ID, $defaultId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAttributeGroupCode()
+ {
+ return $this->getData(self::ATTRIBUTE_GROUP_CODE);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAttributeGroupCode($attributeGroupCode)
+ {
+ return $this->setData(self::ATTRIBUTE_GROUP_CODE, $attributeGroupCode);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getScopeCode()
+ {
+ return $this->getData(self::SCOPE_CODE);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setScopeCode($scopeCode)
+ {
+ return $this->setData(self::SCOPE_CODE, $scopeCode);
+ }
+
/**
* {@inheritdoc}
*
diff --git a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php
index 016ec7dfc35cd..cb9f36fceb53b 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php
@@ -63,7 +63,7 @@ protected function getAttributes($entityType)
$metadata = $this->metadataPool->getMetadata($entityType);
$searchResult = $this->attributeRepository->getList(
$metadata->getEavEntityType(),
- $this->searchCriteriaBuilder->create()
+ $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create()
);
return $searchResult->getItems();
}
diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php
index b750d50f7419f..f9b3a80981695 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php
@@ -477,4 +477,31 @@ public function getSelectCountSql()
$countSelect->columns('COUNT(DISTINCT main_table.attribute_id)');
return $countSelect;
}
+
+ /**
+ * Join table to collection select
+ *
+ * @param string $table
+ * @param string $cond
+ * @param string $cols
+ * @return $this
+ */
+ public function joinLeft($table, $cond, $cols = '*')
+ {
+ if (is_array($table)) {
+ foreach ($table as $k => $v) {
+ $alias = $k;
+ $table = $v;
+ break;
+ }
+ } else {
+ $alias = $table;
+ }
+
+ if (!isset($this->_joinedTables[$alias])) {
+ $this->getSelect()->joinLeft([$alias => $this->getTable($table)], $cond, $cols);
+ $this->_joinedTables[$alias] = true;
+ }
+ return $this;
+ }
}
diff --git a/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php b/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php
index 51c07dd02d603..0a924a6cbb914 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php
@@ -71,9 +71,10 @@ public function __construct(
protected function getAttributes($entityType)
{
$metadata = $this->metadataPool->getMetadata($entityType);
+
$searchResult = $this->attributeRepository->getList(
$metadata->getEavEntityType(),
- $this->searchCriteriaBuilder->create()
+ $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create()
);
return $searchResult->getItems();
}
diff --git a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php
index 2e486e4bfa7c0..dd0627c768672 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php
@@ -71,9 +71,10 @@ public function __construct(
protected function getAttributes($entityType)
{
$metadata = $this->metadataPool->getMetadata($entityType);
+
$searchResult = $this->attributeRepository->getList(
$metadata->getEavEntityType(),
- $this->searchCriteriaBuilder->create()
+ $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create()
);
return $searchResult->getItems();
}
diff --git a/app/code/Magento/Eav/Test/Unit/Model/Adminhtml/Attribute/Validation/Rules/OptionsTest.php b/app/code/Magento/Eav/Test/Unit/Model/Adminhtml/Attribute/Validation/Rules/OptionsTest.php
new file mode 100644
index 0000000000000..9759ef4939f5b
--- /dev/null
+++ b/app/code/Magento/Eav/Test/Unit/Model/Adminhtml/Attribute/Validation/Rules/OptionsTest.php
@@ -0,0 +1,43 @@
+model = $objectManager->getObject(Options::class);
+ }
+
+ public function testToOptionArray()
+ {
+ $this->assertEquals(
+ [
+ ['value' => '', 'label' => __('None')],
+ ['value' => 'validate-number', 'label' => __('Decimal Number')],
+ ['value' => 'validate-digits', 'label' => __('Integer Number')],
+ ['value' => 'validate-email', 'label' => __('Email')],
+ ['value' => 'validate-url', 'label' => __('URL')],
+ ['value' => 'validate-alpha', 'label' => __('Letters')],
+ ['value' => 'validate-alphanum', 'label' => __('Letters (a-z, A-Z) or Numbers (0-9)')]
+ ],
+ $this->model->toOptionArray()
+ );
+ }
+}
diff --git a/app/code/Magento/Eav/Test/Unit/Model/Attribute/GroupRepositoryTest.php b/app/code/Magento/Eav/Test/Unit/Model/Attribute/GroupRepositoryTest.php
index 00b4cfc09b087..97bf81d808cb9 100644
--- a/app/code/Magento/Eav/Test/Unit/Model/Attribute/GroupRepositoryTest.php
+++ b/app/code/Magento/Eav/Test/Unit/Model/Attribute/GroupRepositoryTest.php
@@ -279,10 +279,10 @@ public function testGetList()
false
);
$groupCollectionMock->expects($this->once())->method('getItems')->willReturn([$groupMock]);
- $searchCriteriaMock->expects($this->once())->method('getFilterGroups')->willReturn([$filterGroupMock]);
+ $searchCriteriaMock->expects($this->exactly(2))->method('getFilterGroups')->willReturn([$filterGroupMock]);
- $filterGroupMock->expects($this->once())->method('getFilters')->willReturn([$filterInterfaceMock]);
- $filterInterfaceMock->expects($this->once())->method('getField')->willReturn('attribute_set_id');
+ $filterGroupMock->expects($this->exactly(2))->method('getFilters')->willReturn([$filterInterfaceMock]);
+ $filterInterfaceMock->expects($this->exactly(2))->method('getField')->willReturn('attribute_set_id');
$filterInterfaceMock->expects($this->once())->method('getValue')->willReturn($attributeSetId);
$this->setRepositoryMock->expects($this->once())
diff --git a/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php b/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php
index 35b9be0305019..28c22c342e612 100644
--- a/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php
+++ b/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php
@@ -288,7 +288,6 @@ protected function getModal()
'buttons' => [
[
'text' => __('Cancel'),
- 'class' => 'action-secondary',
'actions' => ['closeModal']
],
[
diff --git a/app/code/Magento/LayeredNavigation/Model/Attribute/Source/FilterableOptions.php b/app/code/Magento/LayeredNavigation/Model/Attribute/Source/FilterableOptions.php
index e5e001db6a74f..94921250ce8a3 100644
--- a/app/code/Magento/LayeredNavigation/Model/Attribute/Source/FilterableOptions.php
+++ b/app/code/Magento/LayeredNavigation/Model/Attribute/Source/FilterableOptions.php
@@ -13,6 +13,10 @@ class FilterableOptions implements \Magento\Framework\Data\OptionSourceInterface
public function toOptionArray()
{
return [
+ [
+ 'value' => 0,
+ 'label' => __('No'),
+ ],
[
'value' => 1,
'label' => __('Filterable (with results)'),
@@ -21,10 +25,6 @@ public function toOptionArray()
'value' => 2,
'label' => __('Filterable (no results)'),
],
- [
- 'value' => 0,
- 'label' => __('No'),
- ],
];
}
}
diff --git a/app/code/Magento/LayeredNavigation/view/adminhtml/ui_component/product_attribute_add_form.xml b/app/code/Magento/LayeredNavigation/view/adminhtml/ui_component/product_attribute_add_form.xml
new file mode 100644
index 0000000000000..d4f97fdd0bda9
--- /dev/null
+++ b/app/code/Magento/LayeredNavigation/view/adminhtml/ui_component/product_attribute_add_form.xml
@@ -0,0 +1,81 @@
+
+
+
diff --git a/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml b/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml
index 5c6cb46b4b026..5c15abf7ec8cb 100755
--- a/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml
+++ b/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml
@@ -46,8 +46,6 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to
getUploaderHtml();
?>
-
@@ -133,9 +131,6 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to
data-form-part=""/>
-
-
getToggleCode() ? $element->getToggleCode() : 'to
+