From 905df37c8b101c29facfb05ebfaa9129820fd179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Mottet=20=28oms=29?= Date: Tue, 10 Aug 2021 13:29:29 +0000 Subject: [PATCH] [FIX] website: add support for svg logo in configurator The configurator's palette selection screen didn't support SVG logo. On SVG upload primary and secondary colors couldn't be extracted and no palette was recommended. It comes from the fact that Python PIL library does not support SVG. We therefore had to convert SVG to PNG before trying to extract primary and secondary colors. A file format checking has been added. If the uplodad file is not an image, a notification is displayed to the user. task-2602521 closes odoo/odoo#76400 X-original-commit: 33f43618f727bdc1239c52c66b73f0f436035774 Signed-off-by: Quentin Smetz (qsm) --- addons/website/models/website.py | 22 +++++++---- .../components/configurator/configurator.js | 38 ++++++++++++++++--- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 9a33efca0da..2cd819a792d 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -307,7 +307,7 @@ class Website(models.Model): @api.model def configurator_init(self): r = dict() - company_id = self.get_current_website().company_id + company = self.get_current_website().company_id configurator_features = self.env['website.configurator.feature'].search([]) r['features'] = [{ 'id': feature.id, @@ -319,8 +319,8 @@ class Website(models.Model): 'module_state': feature.module_id.state, } for feature in configurator_features] r['logo'] = False - if company_id.logo and company_id.logo != company_id._get_logo(): - r['logo'] = company_id.logo.decode('utf-8') + if company.logo and company.logo != company._get_logo(): + r['logo'] = company.logo.decode('utf-8') try: result = self._website_api_rpc('/api/website/1/configurator/industries', {'lang': self.env.user.lang}) r['industries'] = result['industries'] @@ -470,10 +470,18 @@ class Website(models.Model): tour_asset_id = self.env.ref('website.configurator_tour') tour_asset_id.copy({'key': tour_asset_id.key, 'website_id': website.id, 'active': True}) - # logo was generated as base64 url - logo = kwargs.get('logo') - if logo: - website.logo = logo.split(',', 1)[1] + # Set logo from generated attachment or from company's logo + logo_attachment_id = kwargs.get('logo_attachment_id') + company = website.company_id + if logo_attachment_id: + attachment = self.env['ir.attachment'].browse(logo_attachment_id) + attachment.write({ + 'res_model': 'website', + 'res_field': 'logo', + 'res_id': website.id, + }) + elif not logo_attachment_id and company.logo and company.logo != company._get_logo(): + website.logo = company.logo.decode('utf-8') # palette palette = kwargs.get('selected_palette') diff --git a/addons/website/static/src/components/configurator/configurator.js b/addons/website/static/src/components/configurator/configurator.js index 5375083f310..3bd0b41dc81 100644 --- a/addons/website/static/src/components/configurator/configurator.js +++ b/addons/website/static/src/components/configurator/configurator.js @@ -6,6 +6,8 @@ import weUtils from 'web_editor.utils'; import session from 'web.session'; import {ColorpickerWidget} from 'web.Colorpicker'; import {_t, _lt} from 'web.core'; +import {svgToPNG} from 'website.utils'; +import {useService} from "@web/core/utils/hooks"; const {Component, Store, mount, QWeb} = owl; const {useDispatch, useStore, useGetters, useRef} = owl.hooks; @@ -189,6 +191,7 @@ class PaletteSelectionScreen extends Component { this.getters = useGetters(); this.dispatch = useDispatch(); this.logoInputRef = useRef('logoSelectionInput'); + this.notification = useService("notification"); } mounted() { @@ -206,13 +209,32 @@ class PaletteSelectionScreen extends Component { if (logoSelectInput.files.length === 1) { const file = logoSelectInput.files[0]; const data = await utils.getDataURLFromFile(file); - this.dispatch('changeLogo', data); - this.updatePalettes(); + const attachment = await this.rpc({ + route: '/web_editor/attachment/add_data', + params: { + name: 'logo', + data: data.split(',')[1], + is_image: true, + } + }); + if (!attachment.error) { + this.dispatch('changeLogo', data, attachment.id); + this.updatePalettes(); + } else { + this.notification.notify({ + title: file.name, + message: attachment.error, + }); + } } } async updatePalettes() { - let img = this.state.logo.split(',', 2)[1]; + let img = this.state.logo; + if (img.startsWith('data:image/svg+xml')) { + img = await svgToPNG(img); + } + img = img.split(',')[1]; const [color1, color2] = await rpc.query({ model: 'base.document.layout', method: 'extract_image_primary_secondary_colors', @@ -376,8 +398,9 @@ const actions = { state.selectedIndustry = {id, label}; } }, - changeLogo({state}, data) { + changeLogo({state}, data, attachmentId) { state.logo = data; + state.logoAttachmentId = attachmentId; }, selectPalette({state}, paletteName) { if (paletteName === 'recommendedPalette') { @@ -472,6 +495,7 @@ async function getInitialState() { palettes: palettes, features: features, themes: [], + logoAttachmentId: undefined, }); } @@ -508,12 +532,12 @@ async function applyConfigurator(self, themeName) { } const data = { selected_features: selectedFeatures, - logo: self.state.logo, industry_id: self.state.selectedIndustry.id, selected_palette: selectedPalette, theme_name: themeName, website_purpose: WEBSITE_PURPOSES[self.state.selectedPurpose].name, website_type: WEBSITE_TYPES[self.state.selectedType].name, + logo_attachment_id: self.state.logoAttachmentId, }; const resp = await rpc.query({ model: 'website', @@ -540,6 +564,7 @@ async function makeEnvironment() { recommendedPalette: store.state.recommendedPalette, features: store.state.features, logo: store.state.logo, + logoAttachmentId: store.state.logoAttachmentId, }; window.sessionStorage.setItem(SESSION_STORAGE_ITEM_NAME, JSON.stringify(newState)); }); @@ -553,7 +578,8 @@ async function makeEnvironment() { env.loader = qweb.renderToString('website.ThemePreview.Loader', { showTips: true }); - return Object.assign(env, {router, store, qweb}); + const services = Component.env.services; + return Object.assign(env, {router, store, qweb, services}); } async function setup() {