diff --git a/addons/web/static/src/core/utils/binary.js b/addons/web/static/src/core/utils/binary.js index 94bc1c91518..1e033f9a5a2 100644 --- a/addons/web/static/src/core/utils/binary.js +++ b/addons/web/static/src/core/utils/binary.js @@ -7,3 +7,12 @@ export function isBinarySize(value) { return /^\d+(\.\d*)? [^0-9]+$/.test(value); } + +/** + * Get the length necessary for a base64 str to encode maxBytes + * @param {number} maxBytes number of bytes we want to encode in base64 + * @returns {number} number of char + */ +export function toBase64Length(maxBytes) { + return Math.ceil(maxBytes * 4 / 3); +} diff --git a/addons/web/static/src/views/fields/binary/binary_field.js b/addons/web/static/src/views/fields/binary/binary_field.js index 1ecd5a51c02..a32610e932d 100644 --- a/addons/web/static/src/views/fields/binary/binary_field.js +++ b/addons/web/static/src/views/fields/binary/binary_field.js @@ -2,13 +2,16 @@ import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { isBinarySize } from "@web/core/utils/binary"; +import { isBinarySize, toBase64Length } from "@web/core/utils/binary"; import { download } from "@web/core/network/download"; import { standardFieldProps } from "../standard_field_props"; import { FileUploader } from "../file_handler"; import { _t } from "@web/core/l10n/translation"; import { Component } from "@odoo/owl"; + +export const MAX_FILENAME_SIZE_BYTES = 0xFF; // filenames do not exceed 255 bytes on Linux/Windows/MacOS + export class BinaryField extends Component { static template = "web.BinaryField"; static components = { @@ -32,7 +35,7 @@ export class BinaryField extends Component { this.props.record.data[this.props.fileNameField] || this.props.record.data[this.props.name] || "" - ); + ).slice(0, toBase64Length(MAX_FILENAME_SIZE_BYTES)); } update({ data, name }) { diff --git a/addons/web/static/tests/views/fields/binary_field_tests.js b/addons/web/static/tests/views/fields/binary_field_tests.js index bdb75a59329..12af81a9c99 100644 --- a/addons/web/static/tests/views/fields/binary_field_tests.js +++ b/addons/web/static/tests/views/fields/binary_field_tests.js @@ -15,6 +15,8 @@ import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { browser } from "@web/core/browser/browser"; import { errorService } from "@web/core/errors/error_service"; import { registry } from "@web/core/registry"; +import { MAX_FILENAME_SIZE_BYTES } from "@web/views/fields/binary/binary_field"; +import { toBase64Length } from "@web/core/utils/binary"; const BINARY_FILE = "R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7"; @@ -472,6 +474,28 @@ QUnit.module("Fields", (hooks) => { assert.containsNone(target, "button.fa-download"); }); + QUnit.test("Binary filename doesn't exceed 255 bytes", async function (assert) { + const LARGE_BINARY_FILE = BINARY_FILE.repeat(5); + assert.ok((LARGE_BINARY_FILE.length / 4 * 3) > MAX_FILENAME_SIZE_BYTES, + "The initial binary file should be larger than max bytes that can represent the filename"); + serverData.models.partner.fields.document.default = LARGE_BINARY_FILE; + await makeView({ + serverData, + type: "form", + resModel: "partner", + arch: ` +
+ + + `, + }); + assert.strictEqual( + target.querySelector(".o_field_binary input[type=text]").value.length, + toBase64Length(MAX_FILENAME_SIZE_BYTES), + "The filename shouldn't exceed the maximum size in bytes in base64" + ); + }); + QUnit.test("BinaryField filename is updated when using the pager", async function (assert) { serverData.models.partner.records.push( {