diff --git a/addons/web_editor/controllers/main.py b/addons/web_editor/controllers/main.py index 6f93d50c9d7..2a9503db2d0 100644 --- a/addons/web_editor/controllers/main.py +++ b/addons/web_editor/controllers/main.py @@ -147,30 +147,32 @@ class Web_Editor(http.Controller): 'url': url, 'res_model': 'ir.ui.view', }, request.context) - uploads.append({'id': attachment_id}) + uploads += Attachments.read(request.cr, request.uid, [attachment_id], ['name', 'mimetype', 'checksum', 'url'], request.context) else: # images provided try: attachment_ids = [] for c_file in request.httprequest.files.getlist('upload'): - image_data = c_file.read() - image = Image.open(cStringIO.StringIO(image_data)) - w, h = image.size - if w*h > 42e6: # Nokia Lumia 1020 photo resolution - raise ValueError( - u"Image size excessive, uploaded images must be smaller " - u"than 42 million pixel") - - if not disable_optimization and image.format in ('PNG', 'JPEG'): - image_data = tools.image_save_for_web(image) + data = c_file.read() + try: + image = Image.open(cStringIO.StringIO(data)) + w, h = image.size + if w*h > 42e6: # Nokia Lumia 1020 photo resolution + raise ValueError( + u"Image size excessive, uploaded images must be smaller " + u"than 42 million pixel") + if not disable_optimization and image.format in ('PNG', 'JPEG'): + data = tools.image_save_for_web(image) + except IOError, e: + pass attachment_id = Attachments.create(request.cr, request.uid, { 'name': c_file.filename, - 'datas': image_data.encode('base64'), + 'datas': data.encode('base64'), 'datas_fname': c_file.filename, 'res_model': 'ir.ui.view', }, request.context) attachment_ids.append(attachment_id) - uploads = [{'id': id} for id in attachment_ids] + uploads += Attachments.read(request.cr, request.uid, attachment_ids, ['name', 'mimetype', 'checksum', 'url'], request.context) except Exception, e: logger.exception("Failed to upload image to attachment") message = unicode(e) diff --git a/addons/web_editor/static/src/css/editor.css b/addons/web_editor/static/src/css/editor.css index be264550f0f..6c01eca42d3 100644 --- a/addons/web_editor/static/src/css/editor.css +++ b/addons/web_editor/static/src/css/editor.css @@ -694,13 +694,31 @@ .note-image-dialog .existing-attachments .existing-attachment-cell { position: relative; } -.note-image-dialog .existing-attachments .existing-attachment-cell .img { +.note-image-dialog .existing-attachments .existing-attachment-cell .o_attachment_border { border: 1px solid #848490; + text-align: center; +} +.note-image-dialog .existing-attachments .existing-attachment-cell .o_image { + max-width: 100%; + padding-top: 80%; + max-height: 100%; + display: inline-block; +} +.note-image-dialog .existing-attachments .existing-attachment-cell .o_image img { + width: 100%; +} +.note-image-dialog .existing-attachments .existing-attachment-cell .o_webimage { + background-image: none; + padding-top: 0; + width: 100%; + height: initial; + vertical-align: middle; } .note-image-dialog .existing-attachments .existing-attachment-cell .existing-attachment-remove { position: absolute; top: 0; left: 15px; + z-index: 1; cursor: pointer; background: white; padding: 2px; @@ -711,7 +729,7 @@ -webkit-border-bottom-right-radius: 8px; border-bottom-right-radius: 8px; } -.note-image-dialog .existing-attachments .existing-attachment-cell.media_selected > i, .note-image-dialog .existing-attachments .existing-attachment-cell.media_selected > img { +.note-image-dialog .existing-attachments .existing-attachment-cell.media_selected > * { border-width: 5px; border-color: #00f8f8; } @@ -1018,7 +1036,7 @@ html[lang] > body.editor_enable [data-oe-translation-state]:hover { cursor: text; } html[lang] > body.editor_enable [data-oe-translation-state="translated"] { - background: rgba(120, 215, 110, 0.5) !important + background: rgba(120, 215, 110, 0.5) !important; } html[data-translatable] .oe_field_translate { diff --git a/addons/web_editor/static/src/css/editor.sass b/addons/web_editor/static/src/css/editor.sass index da05b02145e..d14601ce8d8 100644 --- a/addons/web_editor/static/src/css/editor.sass +++ b/addons/web_editor/static/src/css/editor.sass @@ -601,13 +601,27 @@ $attachment-border-color: #848490 .existing-attachment-cell position: relative - .img + .o_attachment_border border: 1px solid $attachment-border-color + text-align: center + .o_image + max-width: 100% + padding-top: 80% + max-height: 100% + display: inline-block + img + width: 100% + .o_webimage + background-image: none + padding-top: 0 + width: 100% + height: initial + vertical-align: middle .existing-attachment-remove position: absolute top: 0 left: 15px // padding-left on col-* - + z-index: 1 cursor: pointer background: white padding: 2px @@ -616,7 +630,7 @@ $attachment-border-color: #848490 border-left: none +border-bottom-right-radius(8px) &.media_selected - > i, > img + > * border-width: 5px border-color: rgb(0, 248, 248) diff --git a/addons/web_editor/static/src/js/rte.summernote.js b/addons/web_editor/static/src/js/rte.summernote.js index 467f0042090..dbce1a3757c 100644 --- a/addons/web_editor/static/src/js/rte.summernote.js +++ b/addons/web_editor/static/src/js/rte.summernote.js @@ -398,7 +398,7 @@ dom.isVoid = function (node) { return node && /^BR|^IMG|^HR/.test(node.nodeName.toUpperCase()) || dom.isImg(node); }; dom.isImg = function (node) { - return dom.isImgFont(node) || (node && (node.nodeName === "IMG" || (node.className && node.className.match(/(^|\s)media_iframe_video(\s|$)/i)) )); + return dom.isImgFont(node) || (node && (node.nodeName === "IMG" || (node.className && node.className.match(/(^|\s)(media_iframe_video|o_image)(\s|$)/i)) )); }; dom.isForbiddenNode = function (node) { return $(node).is(".media_iframe_video, .fa, img"); @@ -609,7 +609,7 @@ eventHandler.attach = function (oLayoutInfo, options) { $(document).on('mousedown', summernote_mousedown); $(document).on('mouseup', summernote_mouseup); oLayoutInfo.editor.off('click').on('click', function (e) {e.preventDefault();}); // if the content editable is a link - oLayoutInfo.editor.on('dblclick', 'img, .media_iframe_video, span.fa, i.fa, span.fa', function (event) { + oLayoutInfo.editor.on('dblclick', 'img, .media_iframe_video, span.fa, i.fa, span.fa, a.o_image', function (event) { if (!$(event.target).closest(".note-toolbar").length) { // prevent icon edition of top bar for default summernote new widgets.MediaDialog(oLayoutInfo.editor, event.target).appendTo(document.body); } diff --git a/addons/web_editor/static/src/js/widgets.js b/addons/web_editor/static/src/js/widgets.js index 7f18bde35f0..acd8c859535 100644 --- a/addons/web_editor/static/src/js/widgets.js +++ b/addons/web_editor/static/src/js/widgets.js @@ -101,15 +101,17 @@ var MediaDialog = Dialog.extend({ this.only_images = this.options.only_images || this.options.select_images || (this.media && $(this.media).parent().data("oe-field") === "image"); if (this.only_images) { - this.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden'); + this.$('[href="#editor-media-document"], [href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden'); } if (this.media) { if (this.media.nodeName === "IMG") { this.$('[href="#editor-media-image"]').tab('show'); + } else if ($(this.media).is('a.o_image')) { + this.$('[href="#editor-media-document"]').tab('show'); } else if (this.media.className.match(/(^|\s)media_iframe_video($|\s)/)) { this.$('[href="#editor-media-video"]').tab('show'); - } else if (this.media.parentNode.className.match(/(^|\s)media_iframe_video($|\s)/)) { + } else if (this.media.parentNode.className.match(/(^|\s)media_iframe_video($|\s)/)) { this.media = this.media.parentNode; this.$('[href="#editor-media-video"]').tab('show'); } else if (this.media.className.match(/(^|\s)fa($|\s)/)) { @@ -119,6 +121,8 @@ var MediaDialog = Dialog.extend({ this.imageDialog = new ImageDialog(this, this.media, this.options); this.imageDialog.appendTo(this.$("#editor-media-image")); + this.documentDialog = new ImageDialog(this, this.media, _.extend({'document': true}, this.options)); + this.documentDialog.appendTo(this.$("#editor-media-document")); this.iconDialog = new fontIconsDialog(this, this.media, this.options); this.iconDialog.appendTo(this.$("#editor-media-icon")); this.videoDialog = new VideoDialog(this, this.media, this.options); @@ -130,6 +134,9 @@ var MediaDialog = Dialog.extend({ if ($(event.target).is('[href="#editor-media-image"]')) { self.active = self.imageDialog; self.$('li.search, li.previous, li.next').removeClass("hidden"); + } if ($(event.target).is('[href="#editor-media-document"]')) { + self.active = self.documentDialog; + self.$('li.search, li.previous, li.next').removeClass("hidden"); } else if ($(event.target).is('[href="#editor-media-icon"]')) { self.active = self.iconDialog; self.$('li.search, li.previous, li.next').removeClass("hidden"); @@ -159,6 +166,9 @@ var MediaDialog = Dialog.extend({ if (this.active !== this.imageDialog) { this.imageDialog.clear(); } + if (this.active !== this.documentDialog) { + this.documentDialog.clear(); + } if (this.active !== this.iconDialog) { this.iconDialog.clear(); } @@ -173,23 +183,24 @@ var MediaDialog = Dialog.extend({ this.active.save(); if (this.active.add_class) { - $(this.media).addClass(this.active.add_class); + $(this.active.media).addClass(this.active.add_class); } + var media = this.active.media; - $(document.body).trigger("media-saved", [self.active.media, self.old_media]); - self.trigger("saved", [self.active.media, self.old_media]); + $(document.body).trigger("media-saved", [media, self.old_media]); + self.trigger("saved", [media, self.old_media]); setTimeout(function () { - if (!self.active.media.parentNode) { + if (!media.parentNode) { return; } - range.createFromNode(self.active.media).select(); - click_event(self.active.media, "mousedown"); + range.createFromNode(media).select(); + click_event(media, "mousedown"); if (!this.only_images) { setTimeout(function () { - if($(self.active.media).parent().data("oe-field") !== "image") { - click_event(self.active.media, "click"); + if($(media).parent().data("oe-field") !== "image") { + click_event(media, "click"); } - click_event(self.active.media, "mouseup"); + click_event(media, "mouseup"); },0); } },0); @@ -238,12 +249,14 @@ var ImageDialog = Widget.extend({ 'change input.url': "change_input", 'keyup input.url': "change_input", //'change select.image-style': 'preview_image', - 'click .existing-attachments img': 'select_existing', + 'click .existing-attachments [data-src]': 'select_existing', 'click .existing-attachment-remove': 'try_remove', }), init: function (parent, media, options) { this._super(); this.options = options || {}; + this.accept = this.options.accept || this.options.document ? "*/*" : "image/*"; + this.domain = this.options.domain || ['|', ['mimetype', '=', false], ['mimetype', this.options.document ? 'not in' : 'in', ['image/gif', 'image/jpe', 'image/jpeg', 'image/jpg', 'image/gif', 'image/png']]]; this.parent = parent; this.media = media; this.images = []; @@ -254,13 +267,12 @@ var ImageDialog = Widget.extend({ var self = this; var res = this._super(); var o = { url: null, alt: null }; - // avoid typos, prevent addition of new properties to the object - Object.preventExtensions(o); if ($(this.media).is("img")) { o.url = this.media.getAttribute('src'); - } else { - this.add_class = "img-responsive pull-left"; + } else if ($(this.media).is("a.o_image")) { + o.url = this.media.getAttribute('href').replace(/[?].*/, ''); + o.id = +o.url.match(/\/web\/content\/([0-9]*)/, '')[1]; } this.parent.$(".pager > li").click(function (e) { e.preventDefault(); @@ -271,13 +283,14 @@ var ImageDialog = Widget.extend({ self.page += $target.hasClass('previous') ? -1 : 1; self.display_attachments(); }); - this.set_image(o.url, o.alt); - this.fetch_existing(); + this.fetch_existing().then(function () { + self.set_image(_.find(self.records, function (record) { return record.url === o.url;}) || o); + }); return res; }, - push: function (url, alt, id) { + push: function (attachment) { if (this.options.select_images) { - var img = _.select(this.images, function (v) { return v.url == url;}); + var img = _.select(this.images, function (v) { return v.id == attachment.id;}); if (img.length) { this.images.splice(this.images.indexOf(img[0]),1); return; @@ -285,7 +298,7 @@ var ImageDialog = Widget.extend({ } else { this.images = []; } - this.images.push({'url': url, 'alt': alt, 'id': id}); + this.images.push(attachment); }, save: function () { if (this.options.select_images) { @@ -294,21 +307,33 @@ var ImageDialog = Widget.extend({ } this.parent.trigger("save", this.media); - var img = this.images[0] || { - 'url': this.$(".existing-attachments img:first").attr('src'), - 'alt': this.$(".existing-attachments img:first").attr('alt') - }; - - if (this.media.tagName !== "IMG") { - var media = document.createElement('img'); - $(this.media).replaceWith(media); - this.media = media; + var img = this.images[0]; + if (!img) { + var id = this.$(".existing-attachments [data-src]:first").data('id'); + img = _.find(this.images, function (img) { return img.id === id;}); } - $(this.media).attr('src', img.url).attr('alt', img.alt); + if (!img.is_document) { + if(this.media.tagName !== "IMG") { + var media = document.createElement('img'); + $(this.media).replaceWith(media); + this.media = media; + this.add_class = "img-responsive pull-left"; + } + this.media.setAttribute('src', img.src); + } else { + if (this.media.tagName !== "A") { + $('.note-control-selection').hide(); + var media = document.createElement('a'); + $(this.media).replaceWith(media); + this.media = media; + } + this.media.setAttribute('href', '/web/content/' + img.id + '?unique=' + img.checksum + '&download=true'); + $(this.media).addClass('o_image').attr('title', img.name).attr('data-mimetype', img.mimetype); + } + $(this.media).attr('alt', img.alt); var style = this.style; - this.media.setAttribute('src', img.url); if (style) { this.media.addClass(style); } return this.media; @@ -334,11 +359,9 @@ var ImageDialog = Widget.extend({ self.selected_existing(); }); }, - set_image: function (url, alt, error) { + set_image: function (attachment, error) { var self = this; - if (url) { - this.push(url, alt); - } + this.push(attachment); this.$('input.url').val(''); this.fetch_existing().then(function () { self.selected_existing(); @@ -362,11 +385,16 @@ var ImageDialog = Widget.extend({ delete window[callback]; $form.find('.well > span').remove(); $form.find('.well > div').show(); + _.each(attachments, function (record) { + record.src = record.url || '/web/image/' + record.id; + record.is_document = !(/gif|jpe|jpg|png/.test(record.mimetype)); + }); if (error || !attachments.length) { self.file_selected(null, error || !attachments.length); } + self.images = attachments; for (var i=0; i= records.length)); + this.$el.find('.o_image').each(function () { + var $div = $(this); + if (/gif|jpe|jpg|png/.test($div.data('mimetype'))) { + var $img = $('').addClass('img img-responsive').attr('src', $div.data('url') || $div.data('src')); + $div.addClass('o_webimage').append($img); + } + }); this.selected_existing(); }, select_existing: function (e) { var $img = $(e.currentTarget); - this.push($img.attr('src'), $img.attr('alt'), $img.data('id')); + var attachment = _.find(this.records, function (record) { return record.id === $img.data('id'); }); + this.push(attachment); this.selected_existing(); }, selected_existing: function () { var self = this; this.$('.existing-attachment-cell.media_selected').removeClass("media_selected"); - var $select = this.$('.existing-attachment-cell img').filter(function () { + var $select = this.$('.existing-attachment-cell [data-src]').filter(function () { var $img = $(this); - var url = $img.attr("src"); - return !!_.select(self.images, function (v) { - if (v.url === url) { - if (!v.id) { - v.id = $img.data('id'); - v.alt = $img.attr('alt'); - } - return true; - } - }).length; + return !!_.find(self.images, function (v) { + return (v.url === $img.data("src") || ($img.data("url") && v.url === $img.data("url")) || v.id === $img.data("id")); + }); }); - $select.parent().addClass("media_selected"); + $select.closest('.existing-attachment-cell').addClass("media_selected"); return $select; }, try_remove: function (e) { diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml index 111e91343b1..f8de06e6937 100644 --- a/addons/web_editor/static/src/xml/editor.xml +++ b/addons/web_editor/static/src/xml/editor.xml @@ -41,6 +41,7 @@ diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 9c577e69e5e..7551c531b67 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -370,34 +370,6 @@ ul.oe_menu_editor .disclose { background-color: #ddd; } -.existing-attachments .pager .disabled { - display: none; -} -.existing-attachments .existing-attachment-cell { - position: relative; -} -.existing-attachments .existing-attachment-cell .img { - border: 1px solid #848490; -} -.existing-attachments .existing-attachment-cell .existing-attachment-remove { - position: absolute; - top: 0; - left: 15px; - cursor: pointer; - background: white; - padding: 2px; - border: 1px solid #848490; - border-top: none; - border-left: none; - -moz-border-radius-bottomright: 8px; - -webkit-border-bottom-right-radius: 8px; - border-bottom-right-radius: 8px; -} -.existing-attachments .existing-attachment-cell.media_selected > i, .existing-attachments .existing-attachment-cell.media_selected > img { - border-width: 5px; - border-color: #00f8f8; -} - .oe_carlos_danger { outline: 1px solid red !important; background-color: #ffd9dd !important; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 8a7f65912f4..df617c7aa24 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -316,32 +316,6 @@ ul.oe_menu_editor .font-icons-selected background-color: #ddd -$attachment-border-color: #848490 -.existing-attachments - .pager .disabled - display: none - - .existing-attachment-cell - position: relative - .img - border: 1px solid $attachment-border-color - .existing-attachment-remove - position: absolute - top: 0 - left: 15px // padding-left on col-* - - cursor: pointer - background: white - padding: 2px - border: 1px solid $attachment-border-color - border-top: none - border-left: none - +border-bottom-right-radius(8px) - &.media_selected - > i, > img - border-width: 5px - border-color: rgb(0, 248, 248) - .oe_carlos_danger outline: 1px solid red !important background-color: #ffd9dd !important