From b284293c18b6bcdafe2bda989c5e0a084d1acbd1 Mon Sep 17 00:00:00 2001 From: Benoit Socias Date: Thu, 10 Aug 2023 11:37:37 +0200 Subject: [PATCH] [FIX] website_sale: automatically convert uploaded extra images to webp Since [1] images uploaded through the website builder that are neither GIF nor SVG are converted to the WEBP format by default. The upload of product extra images does not follow this rule. Adding the extra images is done through its own specific route instead of putting the images inside the DOM before the save, it therefore does not run though the general image tools code. This commit converts the uploaded product extra images to WEBP before saving them into the records. It is achieved by following the same approach as when uploading a WEBP in the backend [2], before calling the `/shop/product/extra-images` RPC. The fix is not done by replacing the custom route by an update of the DOM with the images because a full page rendering from the server is needed when adding extra images. Steps to reproduce: - Edit a product through the website builder - Add a JPG extra image - Save the page to leave the website builder - Check the image content => Image was a JPG instead of WEBP. [1]: https://github.com/odoo/odoo/commit/0449fe85cb0e1d639a4e1aeba26e90906f79254d [2]: https://github.com/odoo/odoo/commit/40955397658e4bf858e969668079d0c6a4cef43b task-3460171 closes odoo/odoo#134037 X-original-commit: f970147a2113608d18b47c6f59de6c6338bb2f43 Signed-off-by: Robin Lejeune (role) --- .../static/src/js/website_sale.editor.js | 69 ++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) 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 0f8f9058945..f3ad87072c1 100644 --- a/addons/website_sale/static/src/js/website_sale.editor.js +++ b/addons/website_sale/static/src/js/website_sale.editor.js @@ -789,12 +789,24 @@ options.registry.WebsiteSaleProductPage = options.Class.extend({ message: 'Pictures will be added to the main image. Use "Instant" attributes to set pictures on each variants' }); } + let extraImageEls; const dialog = new ComponentWrapper(this, AttachmentMediaDialogWrapper, { multiImages: true, onlyImages: true, // Kinda hack-ish but the regular save does not get the information we need - save: async () => {}, + save: async (imgEls) => { + extraImageEls = imgEls; + }, extraImageSave: async (attachments) => { + for (const index in attachments) { + const attachment = attachments[index]; + if (attachment.mimetype.startsWith("image/")) { + if (["image/gif", "image/svg+xml"].includes(attachment.mimetype)) { + continue; + } + await this._convertAttachmentToWebp(attachment, extraImageEls[index]); + } + } this._rpc({ route: `/shop/product/extra-images`, params: { @@ -811,6 +823,61 @@ options.registry.WebsiteSaleProductPage = options.Class.extend({ dialog.mount(document.body); }, + async _convertAttachmentToWebp(attachment, imageEl) { + // This method is widely adapted from onFileUploaded in ImageField. + // Upon change, make sure to verify whether the same change needs + // to be applied on both sides. + // Generate alternate sizes and format for reports. + const imgEl = document.createElement("img"); + imgEl.src = imageEl.src; + await new Promise(resolve => imgEl.addEventListener("load", resolve)); + const originalSize = Math.max(imgEl.width, imgEl.height); + const smallerSizes = [1024, 512, 256, 128].filter(size => size < originalSize); + const webpName = attachment.name.replace(/\.(jpe?g|png)$/i, ".webp"); + let referenceId = undefined; + for (const size of [originalSize, ...smallerSizes]) { + const ratio = size / originalSize; + const canvas = document.createElement("canvas"); + canvas.width = imgEl.width * ratio; + canvas.height = imgEl.height * ratio; + const ctx = canvas.getContext("2d"); + ctx.fillStyle = "rgb(255, 255, 255)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(imgEl, 0, 0, imgEl.width, imgEl.height, 0, 0, canvas.width, canvas.height); + const [resizedId] = await this._rpc({ + model: "ir.attachment", + method: "create_unique", + args: [[{ + name: webpName, + description: size === originalSize ? "" : `resize: ${size}`, + datas: canvas.toDataURL("image/webp", 0.75).split(",")[1], + res_id: referenceId, + res_model: "ir.attachment", + mimetype: "image/webp", + }]], + }); + if (size === originalSize) { + attachment.original_id = attachment.id; + attachment.id = resizedId; + attachment.image_src = `/web/image/${resizedId}-autowebp/${attachment.name}`; + attachment.mimetype = "image/webp"; + } + referenceId = referenceId || resizedId; // Keep track of original. + await this._rpc({ + model: "ir.attachment", + method: "create_unique", + args: [[{ + name: webpName.replace(/\.webp$/, ".jpg"), + description: "format: jpeg", + datas: canvas.toDataURL("image/jpeg", 0.75).split(",")[1], + res_id: resizedId, + res_model: "ir.attachment", + mimetype: "image/jpeg", + }]], + }); + } + }, + /** * Removes all extra-images from the product. */