diff --git a/CHANGELOG b/CHANGELOG index 5d25e8c367..06f59e32f3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Support document widget * Support icon of type URL * Allow sending email with existing attachments * Add paths and model context to action data diff --git a/src/common.js b/src/common.js index f8d223d73f..d629570b0f 100644 --- a/src/common.js +++ b/src/common.js @@ -2715,24 +2715,41 @@ } }); + Sao.common.mimetypes = { + 'csv': 'text/csv', + 'doc': 'application/msword', + 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'gif': 'image/gif', + 'html': 'text/html', + 'jpeg': 'image/jpeg', + 'jpg': 'image/jpeg', + 'mpeg': 'video/mpeg', + 'mpg': 'video/mpeg', + 'ods': 'application/vnd.oasis.opendocument.spreadsheet', + 'odt': 'application/vnd.oasis.opendocument.text', + 'ogg': 'audio/ogg', + 'pdf': 'application/pdf', + 'png': 'image/png', + 'svg': 'image/svg+xml', + 'text': 'text/plain', + 'tif': 'image/tif', + 'tiff': 'image/tif', + 'txt': 'text/plain', + 'webp': 'image/webp', + 'xls': 'application/vnd.ms-excel', + 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml': 'application/xml', + 'xpm': 'image/x-xpixmap', + }; + Sao.common.guess_mimetype = function(filename) { - if (/.*odt$/.test(filename)) { - return 'application/vnd.oasis.opendocument.text'; - } else if (/.*ods$/.test(filename)) { - return 'application/vnd.oasis.opendocument.spreadsheet'; - } else if (/.*pdf$/.test(filename)) { - return 'application/pdf'; - } else if (/.*docx$/.test(filename)) { - return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; - } else if (/.*doc/.test(filename)) { - return 'application/msword'; - } else if (/.*xlsx$/.test(filename)) { - return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; - } else if (/.*xls/.test(filename)) { - return 'application/vnd.ms-excel'; - } else { - return 'application/octet-binary'; + for (var ext in Sao.common.mimetypes) { + var re = new RegExp('.*\.' + ext + '$', 'i'); + if (re.test(filename)) { + return Sao.common.mimetypes[ext]; + } } + return 'application/octet-binary'; }; Sao.common.LOCAL_ICONS = [ diff --git a/src/sao.less b/src/sao.less index de6a2a82b9..1cce554fe6 100644 --- a/src/sao.less +++ b/src/sao.less @@ -628,6 +628,13 @@ img.icon { height: 4em; } } + .form-document { + object { + object-fit: scale-down; + object-position: center top; + width: 100%; + } + } label { font-weight: normal; display: inline; diff --git a/src/view/form.js b/src/view/form.js index 02b9d738f5..7cef6245d8 100644 --- a/src/view/form.js +++ b/src/view/form.js @@ -4080,6 +4080,67 @@ function eval_pyson(value){ } }); + Sao.View.Form.Document = Sao.class_(Sao.View.Form.BinaryMixin, { + class_: 'form-document', + expand: true, + init: function(view, attributes) { + Sao.View.Form.Document._super.init.call(this, view, attributes); + + this._blob_url = null; + this.el = jQuery('
', { + 'class': this.class_, + }); + + this.object = jQuery('', { + 'class': 'center-block', + }).appendTo(this.el); + if (attributes.height) { + this.object.css('height', parseInt(attributes.height, 10)); + } else { + this.object.css('height', '50vh'); + } + if (attributes.width) { + this.object.css('width', parseInt(attributes.width, 10)); + } + }, + display: function() { + Sao.View.Form.Document._super.display.call(this); + var data, filename; + var record = this.record; + if (record) { + data = record.model.fields[this.field_name].get_data(record); + } else { + data = jQuery.when(null); + } + var filename_field = this.filename_field; + if (filename_field) { + filename = filename_field.get_client(record); + } + data.done(function(data) { + var url, blob; + if (record !== this.record) { + return; + } + if (!data) { + url = null; + } else { + var mimetype = Sao.common.guess_mimetype(filename); + if (mimetype == 'application/octet-binary') { + mimetype = null; + } + blob = new Blob([data], { + 'type': mimetype, + }); + url = window.URL.createObjectURL(blob); + } + this.object.attr('data', url); + this.object.get(0).onload = function() { + window.URL.revokeObjectURL(url); + }; + }.bind(this)); + }, + }); + Sao.View.Form.URL = Sao.class_(Sao.View.Form.Char, { class_: 'form-url', init: function(view, attributes) { @@ -4924,6 +4985,7 @@ function eval_pyson(value){ 'date': Sao.View.Form.Date, 'datetime': Sao.View.Form.DateTime, 'dict': Sao.View.Form.Dict, + 'document': Sao.View.Form.Document, 'email': Sao.View.Form.Email, 'float': Sao.View.Form.Float, 'html': Sao.View.Form.HTML,