From 7ec41bb9e176cfba838ce2c1cb48b83e86d752bb Mon Sep 17 00:00:00 2001 From: Joseph Caburnay Date: Mon, 14 Nov 2022 11:21:02 +0000 Subject: [PATCH] [FIX] web: image field resets when using manual save **Steps to reproduce:** - go to a product page - replace image (=> image is shown) - click on "Save manually" (the cloud upload icon) - BUG: previous image is displayed. But it was saved: if you move to another record and return then it shows the correct one. **Solution:** Basically, in an image_field: If its props.value is a "binary size", then the image source is a url pointing to a server endpoint and this url is based on the rawCacheKey, otherwise, we use the "base64" of the uploaded image. The proposed solution for the said issue is to invalidate the rawCacheKey during upload. Then, during the calculation of image url (at `getUrl`), we reassign the rawCacheKey with the newest "__last_update" value of the record when needed (if the props.value is a "binary size"). closes odoo/odoo#106236 X-original-commit: 93911daab238e96c8aa9b0a951b9a268d83a9847 Signed-off-by: Lucas Perais (lpe) Signed-off-by: Joseph Caburnay (jcb) --- .../src/views/fields/image/image_field.js | 5 ++ .../tests/views/fields/image_field_tests.js | 69 +++++++++++++++++-- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/addons/web/static/src/views/fields/image/image_field.js b/addons/web/static/src/views/fields/image/image_field.js index 527caf1eb67..1caee3fd471 100644 --- a/addons/web/static/src/views/fields/image/image_field.js +++ b/addons/web/static/src/views/fields/image/image_field.js @@ -76,6 +76,9 @@ export class ImageField extends Component { getUrl(previewFieldName) { if (this.state.isValid && this.props.value) { if (isBinarySize(this.props.value)) { + if (!this.rawCacheKey) { + this.rawCacheKey = this.props.record.data.__last_update; + } return url("/web/image", { model: this.props.record.resModel, id: this.props.record.resId, @@ -96,6 +99,8 @@ export class ImageField extends Component { } onFileUploaded(info) { this.state.isValid = true; + // Invalidate the `rawCacheKey`. + this.rawCacheKey = null; this.props.update(info.data); } onLoadFailed() { diff --git a/addons/web/static/tests/views/fields/image_field_tests.js b/addons/web/static/tests/views/fields/image_field_tests.js index ce36b089db6..b36bbe3aa20 100644 --- a/addons/web/static/tests/views/fields/image_field_tests.js +++ b/addons/web/static/tests/views/fields/image_field_tests.js @@ -19,6 +19,11 @@ const PRODUCT_IMAGE = let serverData; let target; +function getUnique(target) { + const src = target.dataset.src; + return new URL(src).searchParams.get("unique"); +} + QUnit.module("Fields", (hooks) => { hooks.beforeEach(() => { target = getFixture(); @@ -263,6 +268,65 @@ QUnit.module("Fields", (hooks) => { ); }); + QUnit.test( + "clicking save manually after uploading new image should change the unique of the image src", + async function (assert) { + serverData.models.partner.onchanges = { foo: () => {} }; + + const rec = serverData.models.partner.records.find((rec) => rec.id === 1); + rec.document = "3 kb"; + rec.__last_update = "2022-08-05 08:37:00"; // 1659688620000 + + await makeView({ + type: "form", + resModel: "partner", + resId: 1, + serverData, + arch: /* xml */ ` +
+ + + `, + mockRPC(_route, { method, args }) { + if (method === "write") { + args[1].__last_update = "2022-08-05 09:37:00"; // 1659692220000 + args[1].document = "4 kb"; + } + }, + }); + assert.strictEqual( + getUnique(target.querySelector(".o_field_image img")), + "1659688620000" + ); + + await editInput( + target, + "input[type=file]", + new File( + [Uint8Array.from([...atob(MY_IMAGE)].map((c) => c.charCodeAt(0)))], + "fake_file.png", + { type: "png" } + ) + ); + assert.strictEqual( + target.querySelector("div[name=document] img").dataset.src, + `data:image/png;base64,${MY_IMAGE}` + ); + + await editInput(target, ".o_field_widget[name='foo'] input", "grrr"); + assert.strictEqual( + target.querySelector("div[name=document] img").dataset.src, + `data:image/png;base64,${MY_IMAGE}` + ); + + await clickSave(target); + assert.strictEqual( + getUnique(target.querySelector(".o_field_image img")), + "1659692220000" + ); + } + ); + QUnit.test("ImageField: option accepted_file_extensions", async function (assert) { await makeView({ type: "form", @@ -534,11 +598,6 @@ QUnit.module("Fields", (hooks) => { }, }); - function getUnique(target) { - const src = target.dataset.src; - return new URL(src).searchParams.get("unique"); - } - assert.verifySteps(["get_views", "read"]); assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659688620000");