diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 09406b43711..38fc0cad439 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1298,6 +1298,25 @@ var FieldBinaryFile = AbstractFieldBinary.extend({ }, }); +var FieldBinaryDownloadLink = AbstractFieldBinary.extend({ + template: 'FieldBinaryDownloadLink', + events: _.extend({}, AbstractFieldBinary.prototype.events, { + 'click': function (ev) { + ev.stopPropagation(); + }, + }), + supportedFieldTypes: ['binary'], + init: function () { + this._super.apply(this, arguments); + this.filename = _t("Binary File"); + if (this.nodeOptions.filename && this.recordData[this.nodeOptions.filename]) { + this.filename = this.recordData[this.nodeOptions.filename]; + } + this.url = "data:application/octet-stream;base64," + this.value; + this.text = _t("Download"); + }, +}); + var PriorityWidget = AbstractField.extend({ // the current implementation of this widget makes it // only usable for fields of type selection @@ -2528,6 +2547,7 @@ return { TranslatableFieldMixin: TranslatableFieldMixin, DebouncedField: DebouncedField, FieldEmail: FieldEmail, + FieldBinaryDownloadLink: FieldBinaryDownloadLink, FieldBinaryFile: FieldBinaryFile, FieldBinaryImage: FieldBinaryImage, FieldBoolean: FieldBoolean, diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js index 6692663d5f9..85b4fc1187f 100644 --- a/addons/web/static/src/js/fields/field_registry.js +++ b/addons/web/static/src/js/fields/field_registry.js @@ -33,6 +33,7 @@ registry .add('email', basic_fields.FieldEmail) .add('phone', basic_fields.FieldPhone) .add('url', basic_fields.UrlWidget) + .add('download_link', basic_fields.FieldBinaryDownloadLink) .add('image', basic_fields.FieldBinaryImage) .add('binary', basic_fields.FieldBinaryFile) .add('monetary', basic_fields.FieldMonetary) diff --git a/addons/web/static/src/js/fields/field_utils.js b/addons/web/static/src/js/fields/field_utils.js index 6a6ad5a7a98..812a7f380f7 100644 --- a/addons/web/static/src/js/fields/field_utils.js +++ b/addons/web/static/src/js/fields/field_utils.js @@ -27,6 +27,20 @@ var _t = core._t; // Formatting //------------------------------------------------------------------------------ +/** + * + * @param {string} value base64 representation of the binary + * @param {Object} [field] + * a description of the field (note: this parameter is ignored) + * @param {Object} [options] additional options (note: this parameter is ignored) + */ +function formatBinary(value, field, options) { + if (!value) { + return ''; + } + return utils.human_size(value.length / 1.37); // base64 estimated size: http://en.wikipedia.org/wiki/Base64#MIME +} + /** * @todo Really? it returns a jQuery element... We should try to avoid this and * let DOM utility functions handle this directly. And replace this with a @@ -561,7 +575,7 @@ function parseMany2one(value) { return { format: { - binary: _.identity, // todo + binary: formatBinary, boolean: formatBoolean, char: formatChar, date: formatDate, diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 5df2c0ec3f7..8c69bb3439a 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -924,6 +924,9 @@ + + + ' + + '' + + '' + + '', + res_id: 1, + }); + + assert.ok(form.$('.o_data_cell').first().hasClass("o_download_link_cell"), + "this should be a download link cell"); + assert.ok(form.$('.o_data_cell').first().find('a').eq(0), + "there should be a link in the cell"); + assert.strictEqual(form.$('.o_data_cell').first().find('a').eq(0).attr('download'), + "document.txt", + "it should download a file named 'document.txt'"); + assert.strictEqual(form.$('.o_data_cell').first().find('a').eq(0).attr('href').trim(), + "data:application/octet-stream;base64,coucou==", + "it should have the appropriate link of a binary"); + assert.strictEqual(form.$('.o_data_cell').first().find('a').eq(0).text(), + "Download", + "the text of the link should be 'Download'"); + + form.destroy(); + }); + QUnit.test('text field rendering in list view', function (assert) { assert.expect(1); diff --git a/addons/web/static/tests/fields/field_utils_tests.js b/addons/web/static/tests/fields/field_utils_tests.js index 1f8c27e7192..e24571ea1d8 100644 --- a/addons/web/static/tests/fields/field_utils_tests.js +++ b/addons/web/static/tests/fields/field_utils_tests.js @@ -127,6 +127,15 @@ QUnit.test('format one2many', function(assert) { assert.strictEqual(fieldUtils.format.one2many({data: [1, 2]}), '2 records'); }); +QUnit.test('format binary', function (assert) { + assert.expect(1); + + // base64 estimated size (bytes) = value.length / 1.37 (http://en.wikipedia.org/wiki/Base64#MIME) + // Here: 4 / 1.37 = 2.91970800 => 2.92 (rounded 2 decimals by utils.human_size) + assert.strictEqual(fieldUtils.format.binary('Cg=='), '2.92 Bytes'); + +}); + QUnit.test('parse float', function(assert) { assert.expect(10);