diff --git a/addons/website_sale/__manifest__.py b/addons/website_sale/__manifest__.py index 6b365ad7eed..1945d050a60 100644 --- a/addons/website_sale/__manifest__.py +++ b/addons/website_sale/__manifest__.py @@ -31,6 +31,7 @@ 'views/website_sale_visitor_views.xml', 'views/base_unit_view.xml', 'views/product_product_add.xml', + 'views/website_views.xml', ], 'demo': [ 'data/demo.xml', diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index abbcc36c919..f99613599cc 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -1207,12 +1207,21 @@ class WebsiteSale(http.Controller): raise NotFound() current_website = request.env['website'].get_current_website() - if "ppg" in options: - current_website.shop_ppg = options["ppg"] or 1 - if "ppr" in options: - current_website.shop_ppr = options["ppr"] - if "default_sort" in options: - current_website.shop_default_sort = options["default_sort"] + # Restrict options we can write to. + writable_fields = { + 'shop_ppg', 'shop_ppr', 'shop_default_sort', + 'product_page_image_layout', 'product_page_image_width', + 'product_page_grid_columns', 'product_page_image_spacing' + } + # Default ppg to 1. + if 'ppg' in options and not options['ppg']: + options['ppg'] = 1 + if 'product_page_grid_columns' in options: + options['product_page_grid_columns'] = int(options['product_page_grid_columns']) + + write_vals = {k: v for k, v in options.items() if k in writable_fields} + if write_vals: + current_website.write(write_vals) def order_lines_2_google_api(self, order_lines): """ Transforms a list of order lines into a dict for google analytics """ diff --git a/addons/website_sale/controllers/variant.py b/addons/website_sale/controllers/variant.py index 3618a22e1d1..692b8363903 100644 --- a/addons/website_sale/controllers/variant.py +++ b/addons/website_sale/controllers/variant.py @@ -18,11 +18,13 @@ class WebsiteSaleVariantController(VariantController): if request.website.google_analytics_key: combination['product_tracking_info'] = request.env['product.template'].get_google_analytics_data(combination) - carousel_view = request.env['ir.ui.view']._render_template('website_sale.shop_product_carousel', values={ - 'product': request.env['product.template'].browse(combination['product_template_id']), - 'product_variant': request.env['product.product'].browse(combination['product_id']), - }) - combination['carousel'] = carousel_view + if request.website.product_page_image_width != 'none': + carousel_view = request.env['ir.ui.view']._render_template('website_sale.shop_product_images', values={ + 'product': request.env['product.template'].browse(combination['product_template_id']), + 'product_variant': request.env['product.product'].browse(combination['product_id']), + 'website': request.env['website'].get_current_website(), + }) + combination['carousel'] = carousel_view return combination @http.route(auth="public") diff --git a/addons/website_sale/models/website.py b/addons/website_sale/models/website.py index dee0a601cd8..eb62f6023ee 100644 --- a/addons/website_sale/models/website.py +++ b/addons/website_sale/models/website.py @@ -80,6 +80,27 @@ class Website(models.Model): ], default='optional') + product_page_image_layout = fields.Selection([ + ('carousel', 'Carousel'), + ('grid', 'Grid'), + ], default='carousel', required=True, + ) + product_page_grid_columns = fields.Integer(default=2) + product_page_image_width = fields.Selection([ + ('none', 'Hidden'), + ('50_pc', '50 %'), + ('66_pc', '66 %'), + ('100_pc', '100 %'), + ], default='50_pc', required=True, + ) + product_page_image_spacing = fields.Selection([ + ('none', 'None'), + ('small', 'Small'), + ('medium', 'Medium'), + ('big', 'Big'), + ], default='small', required=True, + ) + @api.depends('all_pricelist_ids') def _compute_pricelist_ids(self): for website in self: @@ -462,6 +483,33 @@ class Website(models.Model): result.append(self.env['product.template']._search_get_detail(self, order, options)) return result + def _get_product_page_proportions(self): + """ + Returns the number of columns (css) that both the images and the product details should take. + """ + self.ensure_one() + + return { + 'none': (0, 12), + '50_pc': (6, 6), + '66_pc': (8, 4), + '100_pc': (12, 12), + }.get(self.product_page_image_width) + + def _get_product_page_grid_image_classes(self): + spacing_map = { + 'none': 'p-0', + 'small': 'p-2', + 'medium': 'p-3', + 'big': 'p-4', + } + columns_map = { + 1: 'col-12', + 2: 'col-6', + 3: 'col-4', + } + return spacing_map.get(self.product_page_image_spacing) + ' ' +\ + columns_map.get(self.product_page_grid_columns) class WebsiteSaleExtraField(models.Model): _name = 'website.sale.extra.field' diff --git a/addons/website_sale/static/src/img/snippet_options/image-width-100.svg b/addons/website_sale/static/src/img/snippet_options/image-width-100.svg new file mode 100644 index 00000000000..9dd87e5bdc2 --- /dev/null +++ b/addons/website_sale/static/src/img/snippet_options/image-width-100.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/website_sale/static/src/img/snippet_options/image-width-50.svg b/addons/website_sale/static/src/img/snippet_options/image-width-50.svg new file mode 100644 index 00000000000..02c063399d9 --- /dev/null +++ b/addons/website_sale/static/src/img/snippet_options/image-width-50.svg @@ -0,0 +1,4 @@ + + + + diff --git a/addons/website_sale/static/src/img/snippet_options/image-width-66.svg b/addons/website_sale/static/src/img/snippet_options/image-width-66.svg new file mode 100644 index 00000000000..cbded7aea88 --- /dev/null +++ b/addons/website_sale/static/src/img/snippet_options/image-width-66.svg @@ -0,0 +1,4 @@ + + + + diff --git a/addons/website_sale/static/src/img/snippet_options/image-width-none.svg b/addons/website_sale/static/src/img/snippet_options/image-width-none.svg new file mode 100644 index 00000000000..a64c740dca2 --- /dev/null +++ b/addons/website_sale/static/src/img/snippet_options/image-width-none.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/website_sale/static/src/js/website_sale.editor.js b/addons/website_sale/static/src/js/website_sale.editor.js index 74ca08e79cd..e9af1eef78b 100644 --- a/addons/website_sale/static/src/js/website_sale.editor.js +++ b/addons/website_sale/static/src/js/website_sale.editor.js @@ -234,7 +234,7 @@ options.registry.WebsiteSaleGridLayout = options.Class.extend({ return this._rpc({ route: '/shop/config/website', params: { - 'ppg': ppg, + 'shop_ppg': ppg, }, }); }, @@ -246,7 +246,7 @@ options.registry.WebsiteSaleGridLayout = options.Class.extend({ this._rpc({ route: '/shop/config/website', params: { - 'ppr': this.ppr, + 'shop_ppr': this.ppr, }, }); }, @@ -258,7 +258,7 @@ options.registry.WebsiteSaleGridLayout = options.Class.extend({ this._rpc({ route: '/shop/config/website', params: { - 'default_sort': this.default_sort, + 'shop_default_sort': this.default_sort, }, }); }, @@ -643,6 +643,96 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({ } }); +options.registry.WebsiteSaleProductPage = options.Class.extend({ + + _updateWebsiteConfig(params) { + this._rpc({ + route: '/shop/config/website', + params, + }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); + }, + + /** + * @override + */ + setImageWidth(previewMode, widgetValue, params) { + this._updateWebsiteConfig({ + product_page_image_width: widgetValue, + }); + }, + + /** + * @override + */ + setImageLayout(previewMode, widgetValue, params) { + this._updateWebsiteConfig({ + product_page_image_layout: widgetValue, + }); + }, + + /** + * @override + */ + async _computeWidgetState(methodName, params) { + switch (methodName) { + case 'setImageWidth': + return this.$target.data('image_width'); + case 'setImageLayout': + return this.$target.data('image_layout'); + } + return this._super(...arguments); + } +}); + +options.registry.WebsiteSaleProductPageGrid = options.Class.extend({ + /** + * @override + */ + setSpacing(previewMode, widgetValue, params) { + const spacing = { + 0: 'none', + 1: 'small', + 2: 'medium', + 3: 'big', + }[widgetValue]; + this._rpc({ + route: '/shop/config/website', + params: { + 'product_page_image_spacing': spacing, + }, + }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); + this.$target.data('image_spacing', spacing); + }, + + setColumns(previewMode, widgetValue, params) { + this._rpc({ + route: '/shop/config/website', + params: { + 'product_page_grid_columns': widgetValue, + }, + }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); + this.$target.data('grid_columns', widgetValue); + }, + + /** + * @override + */ + async _computeWidgetState(methodName, params) { + switch (methodName) { + case 'setSpacing': + return { + 'none': 0, + 'small': 1, + 'medium': 2, + 'big': 3, + }[this.$target.data('image_spacing')]; + case 'setColumns': + return this.$target.data('grid_columns'); + } + return this._super(methodName, params); + } +}); + options.registry.WebsiteSaleProductAttribute = options.Class.extend({ /** * @override diff --git a/addons/website_sale/static/src/js/website_sale_utils.js b/addons/website_sale/static/src/js/website_sale_utils.js index af2e96aff98..ba5cdc842c7 100644 --- a/addons/website_sale/static/src/js/website_sale_utils.js +++ b/addons/website_sale/static/src/js/website_sale_utils.js @@ -36,7 +36,10 @@ const cartHandlerMixin = { }).then(async data => { sessionStorage.setItem('website_sale_cart_quantity', data.cart_quantity); if (data.cart_quantity && (data.cart_quantity !== parseInt($(".my_cart_quantity").text()))) { - await animateClone($('header .o_wsale_my_cart').first(), this.$itemImgContainer, 25, 40); + // No animation if the product's page images are hidden + if ($('div[data-image_width]').data('image_width') !== 'none') { + await animateClone($('header .o_wsale_my_cart').first(), this.$itemImgContainer, 25, 40); + } updateCartNavBar(data); } }); diff --git a/addons/website_sale/views/snippets/snippets.xml b/addons/website_sale/views/snippets/snippets.xml index bfc265951d8..483e0a9bb95 100644 --- a/addons/website_sale/views/snippets/snippets.xml +++ b/addons/website_sale/views/snippets/snippets.xml @@ -185,6 +185,18 @@ data-reload="/"/> +
+ + + + + + + + Carousel + Grid + +
None @@ -196,6 +208,14 @@
+
+ + + 1 + 2 + 3 + +
Radio diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index 383eead9fc0..02c331b18f2 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -624,11 +624,14 @@
-
-
- +
+ +
+
-
+

Product Name