diff options
| author | Pedro M. Baeza <pedro.baeza@gmail.com> | 2018-08-27 23:36:33 +0200 |
|---|---|---|
| committer | Olivier Dony <odo@odoo.com> | 2018-10-03 17:48:01 +0200 |
| commit | 1be50fdeafcd2b94f7b2f47d6e5bc4b6ebaceadd (patch) | |
| tree | 4489771b5b5dcf02212a4dfdce4e8c2c80e3a6b5 /addons | |
| parent | 376361c15c8d2be15a2579ffd0432f31d745deb8 (diff) | |
[ADD] *: support SVG images
Introduce official support for SVG files in the framework, including the
following parts:
1. When client-side SVG images are uploaded, the content is displayed until
you save using data URI scheme according RFC 2397 [1]. This scheme requires
to specify content format. Using hardcoded "image/png" works for all images
types except SVG.
Type-sniffing is done using "magic byte" detection via the first base64
encode byte, so that the proper data URI scheme can be used.
This should not cause SVG-related security problems as the file is
displayed through `<img>` tag, which does not allow SVG scripting [2].
2. Make /web/image controller compatible with SVG
3. Add support for SVG files for company logo, which uses a dedicated
controller.
4. Resizing of SVG files is a no-op, as it makes little sense for a
vector-based format. We also want to avoid micro-alterations to the SVG
document (in "natural" viewport parameters) as we would store multiple
copies of the files in the filestore.
5. Because SVG files are inherently dangerous, upload of SVG files is
restricted to administrators, either by blocking it directly before
saving it in the database (binary fields with attachment=False), or by
neutering them to text/plain mimetype (for binary fields with
attachment=True)
6. Add tests for the SVG upload cases and for the non-admin uploads.
[1] https://tools.ietf.org/html/rfc2397
[2] https://www.w3.org/wiki/SVG_Security
Closes #26635
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/web/controllers/main.py | 8 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/basic_fields.js | 9 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/kanban/kanban_record.js | 9 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/widgets/widgets.js | 1 | ||||
| -rw-r--r-- | addons/website_slides/static/src/js/slides_upload.js | 1 |
5 files changed, 24 insertions, 4 deletions
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index c2027984c42..ed2e9f13062 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -37,6 +37,7 @@ import odoo.modules.registry from odoo.api import call_kw, Environment from odoo.modules import get_resource_path from odoo.tools import crop_image, topological_sort, html_escape, pycompat +from odoo.tools.mimetypes import guess_mimetype from odoo.tools.translate import _ from odoo.tools.misc import str2bool, xlwt, file_open from odoo.tools.safe_eval import safe_eval @@ -1218,8 +1219,11 @@ class Binary(http.Controller): if row and row[0]: image_base64 = base64.b64decode(row[0]) image_data = io.BytesIO(image_base64) - imgext = '.' + (imghdr.what(None, h=image_base64) or 'png') - response = http.send_file(image_data, filename=imgname + imgext, mtime=row[1]) + mimetype = guess_mimetype(image_base64, default='image/png') + imgext = '.' + mimetype.split('/')[1] + if imgext == '.svg+xml': + imgext = '.svg' + response = http.send_file(image_data, filename=imgname + imgext, mimetype=mimetype, mtime=row[1]) else: response = http.send_file(placeholder('nologo.png')) except Exception: diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 0c28f0f4b61..12b7b8f302c 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1471,12 +1471,19 @@ var FieldBinaryImage = AbstractFieldBinary.extend({ }, }), supportedFieldTypes: ['binary'], + file_type_magic_word: { + '/': 'jpg', + 'R': 'gif', + 'i': 'png', + 'P': 'svg+xml', + }, _render: function () { var self = this; var url = this.placeholder; if (this.value) { if (!utils.is_bin_size(this.value)) { - url = 'data:image/png;base64,' + this.value; + // Use magic-word technique for detecting image type + url = 'data:image/' + (this.file_type_magic_word[this.value[0]] || 'png') + ';base64,' + this.value; } else { url = session.url('/web/image', { model: this.model, diff --git a/addons/web/static/src/js/views/kanban/kanban_record.js b/addons/web/static/src/js/views/kanban/kanban_record.js index 9be296e3e31..7013464ea87 100644 --- a/addons/web/static/src/js/views/kanban/kanban_record.js +++ b/addons/web/static/src/js/views/kanban/kanban_record.js @@ -166,6 +166,12 @@ var KanbanRecord = Widget.extend({ var colorID = this._getColorID(variable); return KANBAN_RECORD_COLORS[colorID]; }, + file_type_magic_word: { + '/': 'jpg', + 'R': 'gif', + 'i': 'png', + 'P': 'svg+xml', + }, /** * @private * @param {string} model the name of the model @@ -179,7 +185,8 @@ var KanbanRecord = Widget.extend({ options = options || {}; var url; if (this.record[field] && this.record[field].value && !utils.is_bin_size(this.record[field].value)) { - url = 'data:image/png;base64,' + this.record[field].value; + // Use magic-word technique for detecting image type + url = 'data:image/' + this.file_type_magic_word[this.record[field].value[0]] + ';base64,' + this.record[field].value; } else if (this.record[field] && ! this.record[field].value) { url = "/web/static/src/img/placeholder.png"; } else { diff --git a/addons/web_editor/static/src/js/widgets/widgets.js b/addons/web_editor/static/src/js/widgets/widgets.js index dd0982bf3be..2c06743b361 100644 --- a/addons/web_editor/static/src/js/widgets/widgets.js +++ b/addons/web_editor/static/src/js/widgets/widgets.js @@ -363,6 +363,7 @@ var ImageWidget = MediaWidget.extend({ if (!noRender) { this.$('input.url').val('').trigger('input').trigger('change'); } + // TODO: Expand this for adding SVG var domain = this.domain.concat(['|', ['mimetype', '=', false], ['mimetype', this.options.document ? 'not in' : 'in', ['image/gif', 'image/jpe', 'image/jpeg', 'image/jpg', 'image/gif', 'image/png']]]); if (needle && needle.length) { domain.push('|', ['datas_fname', 'ilike', needle], ['name', 'ilike', needle]); diff --git a/addons/website_slides/static/src/js/slides_upload.js b/addons/website_slides/static/src/js/slides_upload.js index 8aa18dc7381..aa638882930 100644 --- a/addons/website_slides/static/src/js/slides_upload.js +++ b/addons/website_slides/static/src/js/slides_upload.js @@ -298,6 +298,7 @@ var SlideDialog = Widget.extend({ }); return res; }, + // TODO: Remove this part, as now SVG support in image resize tools is included //Python PIL does not support SVG, so converting SVG to PNG svg_to_png: function () { var img = this.$el.find("img#slide-image")[0]; |
