[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) <role@odoo.com>
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user