diff options
| author | Christophe Simonis <chs@odoo.com> | 2018-06-29 12:46:00 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2018-06-29 12:46:00 +0200 |
| commit | 49edee1f4bd74bc342863d8ef50eb86de0b4c55f (patch) | |
| tree | 20f449f08ba1f4a548c9cb2d82f67f2545048419 /addons/web_editor | |
| parent | 4a08388492c6667b7f4a11f1397b8557c355b208 (diff) | |
| parent | d54a3726eca6ad48d969d5b4a6e559f783809832 (diff) | |
[MERGE] forward port branch saas-11.4 up to d54a3726eca
Diffstat (limited to 'addons/web_editor')
| -rw-r--r-- | addons/web_editor/controllers/main.py | 14 | ||||
| -rw-r--r-- | addons/web_editor/models/ir_qweb.py | 1 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/editor/editor.js | 54 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/editor/snippets.editor.js | 147 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/editor/transcoder.js | 18 | ||||
| -rw-r--r-- | addons/web_editor/static/src/js/widgets/widgets.js | 186 | ||||
| -rw-r--r-- | addons/web_editor/static/src/scss/web_editor.ui.scss | 8 | ||||
| -rw-r--r-- | addons/web_editor/static/src/xml/editor.xml | 4 |
8 files changed, 220 insertions, 212 deletions
diff --git a/addons/web_editor/controllers/main.py b/addons/web_editor/controllers/main.py index 688ca74aec6..22a1e514d88 100644 --- a/addons/web_editor/controllers/main.py +++ b/addons/web_editor/controllers/main.py @@ -255,11 +255,14 @@ class Web_Editor(http.Controller): elif image_id: record = request.env['ir.attachment'].browse(image_id) result = { - 'id': record.id, 'mimetype': record.mimetype, } + # If we received the image ID and that image has an associated URL + # field, this should be a crop image attachment, so we return the ID + # and URL to confirm if image_id and record.url: - result['src'] = record.url + result['id'] = record.id + result['originalSrc'] = record.url return result ## The get_assets_editor_resources route is in charge of transmitting the resources the assets @@ -270,7 +273,7 @@ class Web_Editor(http.Controller): ## @param bundles - True if the bundles views must be fetched (default to False) ## @param bundles_restriction - Names of the bundle in which to look for scss files (if empty, search in all of them) ## @returns a dictionary with views info in the views key and style info in the scss key - @http.route("/web_editor/get_assets_editor_resources", type="json", auth="user") + @http.route("/web_editor/get_assets_editor_resources", type="json", auth="user", website=True) def get_assets_editor_resources(self, key, get_views=True, get_scss=True, bundles=False, bundles_restriction=[]): # Related views must be fetched if the user wants the views and/or the style views = request.env["ir.ui.view"].get_related_views(key, bundles=bundles) @@ -387,16 +390,17 @@ class Web_Editor(http.Controller): # Check if the file to save had already been modified custom_attachment = IrAttachment.search([("url", "=", custom_url)]) + datas = base64.b64encode((content or "\n").encode("utf-8")) if custom_attachment: # If it was already modified, simply override the corresponding attachment content - custom_attachment.write({"datas": base64.b64encode(content.encode("utf-8"))}) + custom_attachment.write({"datas": datas}) else: # If not, create a new attachment to copy the original scss file content, with its modifications IrAttachment.create(dict( name = custom_url, type = "binary", mimetype = "text/scss", - datas = base64.b64encode(content.encode("utf-8")), + datas = datas, datas_fname = url.split("/")[-1], url = custom_url, # Having an attachment of "binary" type with an non empty "url" field # is quite of an hack. This allows to fetch the "datas" field by adding diff --git a/addons/web_editor/models/ir_qweb.py b/addons/web_editor/models/ir_qweb.py index 759590a379f..04b0253c3a2 100644 --- a/addons/web_editor/models/ir_qweb.py +++ b/addons/web_editor/models/ir_qweb.py @@ -175,6 +175,7 @@ class Contact(models.AbstractModel): @api.model def attributes(self, record, field_name, options, values): attrs = super(Contact, self).attributes(record, field_name, options, values) + options.pop('template_options') # remove options not specific to this widget attrs['data-oe-contact-options'] = json.dumps(options) return attrs diff --git a/addons/web_editor/static/src/js/editor/editor.js b/addons/web_editor/static/src/js/editor/editor.js index 95831dd306a..7536c1f5516 100644 --- a/addons/web_editor/static/src/js/editor/editor.js +++ b/addons/web_editor/static/src/js/editor/editor.js @@ -145,7 +145,9 @@ var EditorMenuBar = Widget.extend({ save: function (reload) { var self = this; this.snippetsMenu.cleanForSave(); - return this.rte.save().then(function () { + this._saveCroppedImages().then(function () { + return self.rte.save(); + }).then(function () { if (reload !== false) { return self._reload(); } @@ -171,6 +173,56 @@ var EditorMenuBar = Widget.extend({ } return $.Deferred(); }, + /** + * @private + */ + _saveCroppedImages: function () { + var self = this; + var defs = _.map(this.rte.editable().find('.o_cropped_img_to_save'), function (croppedImg) { + var $croppedImg = $(croppedImg); + $croppedImg.removeClass('o_cropped_img_to_save'); + + var resModel = $croppedImg.data('crop:resModel'); + var resID = $croppedImg.data('crop:resID'); + var cropID = $croppedImg.data('crop:id'); + var mimetype = $croppedImg.data('crop:mimetype'); + var originalSrc = $croppedImg.data('crop:originalSrc'); + + var datas = $croppedImg.attr('src').split(',')[1]; + + if (!cropID) { + var name = originalSrc + '.crop'; + return self._rpc({ + model: 'ir.attachment', + method: 'create', + args: [{ + res_model: resModel, + res_id: resID, + name: name, + datas_fname: name, + datas: datas, + mimetype: mimetype, + url: originalSrc, // To save the original image that was cropped + }], + }).then(function (attachmentID) { + return self._rpc({ + model: 'ir.attachment', + method: 'generate_access_token', + args: [[attachmentID]], + }).then(function (access_token) { + $croppedImg.attr('src', '/web/image/' + attachmentID + '?access_token=' + access_token[0]); + }); + }); + } else { + return self._rpc({ + model: 'ir.attachment', + method: 'write', + args: [[cropID], {datas: datas}], + }); + } + }); + return $.when.apply($, defs); + }, //-------------------------------------------------------------------------- // Handlers diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js index 708f576ebc8..cbaa7a2fbbc 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -90,6 +90,7 @@ var SnippetEditor = Widget.extend({ * @override */ destroy: function () { + this.cleanForSave(); this._super.apply(this, arguments); this.$target.removeData('snippet-editor'); }, @@ -136,6 +137,57 @@ var SnippetEditor = Widget.extend({ this.$el.toggleClass('o_top_cover', offset.top < 15); }, /** + * Removes the associated snippet from the DOM and destroys the associated + * editor (itself). + */ + removeSnippet: function () { + this.toggleFocus(false); + + this.trigger_up('call_for_each_child_snippet', { + $snippet: this.$target, + callback: function (editor, $snippet) { + for (var i in editor.styles) { + editor.styles[i].onRemove(); + } + }, + }); + + var $parent = this.$target.parent(); + this.$target.find('*').andSelf().tooltip('destroy'); + this.$target.remove(); + this.$el.remove(); + + var node = $parent[0]; + if (node && node.firstChild) { + $.summernote.core.dom.removeSpace(node, node.firstChild, 0, node.lastChild, 1); + if (!node.firstChild.tagName && node.firstChild.textContent === ' ') { + node.removeChild(node.firstChild); + } + } + + if ($parent.closest(':data("snippet-editor")').length) { + while (!$parent.data('snippet-editor')) { + var $nextParent = $parent.parent(); + if ($parent.children().length === 0 && $parent.text().trim() === '' && !$parent.hasClass('oe_structure')) { + $parent.remove(); + } + $parent = $nextParent; + } + if ($parent.children().length === 0 && $parent.text().trim() === '' && !$parent.hasClass('oe_structure')) { + _.defer(function () { + $parent.data('snippet-editor').removeSnippet(); + }); + } + } + + // clean editor if they are image or table in deleted content + $('.note-control-selection').hide(); + $('.o_table_handler').remove(); + + this.trigger_up('snippet_removed'); + this.destroy(); + }, + /** * Displays/Hides the editor overlay and notifies the associated snippet * options. Note: when it is displayed, this is here that the parent * snippet options are moved to the editor overlay. @@ -259,59 +311,6 @@ var SnippetEditor = Widget.extend({ return $.when.apply($, defs); }, - /** - * Removes the associated snippet from the DOM and destroys the associated - * editor (itself). - * - * @private - */ - _removeSnippet: function () { - this.toggleFocus(false); - - this.trigger_up('call_for_each_child_snippet', { - $snippet: this.$target, - callback: function (editor, $snippet) { - for (var i in editor.styles) { - editor.styles[i].onRemove(); - } - }, - }); - - var $parent = this.$target.parent(); - this.$target.find('*').andSelf().tooltip('destroy'); - this.$target.remove(); - this.$el.remove(); - - var node = $parent[0]; - if (node && node.firstChild) { - $.summernote.core.dom.removeSpace(node, node.firstChild, 0, node.lastChild, 1); - if (!node.firstChild.tagName && node.firstChild.textContent === ' ') { - node.removeChild(node.firstChild); - } - } - - if ($parent.closest(':data("snippet-editor")').length) { - while (!$parent.data('snippet-editor')) { - var $nextParent = $parent.parent(); - if ($parent.children().length === 0 && $parent.text().trim() === '' && !$parent.hasClass('oe_structure')) { - $parent.remove(); - } - $parent = $nextParent; - } - if ($parent.children().length === 0 && $parent.text().trim() === '' && !$parent.hasClass('oe_structure')) { - _.defer(function () { - $parent.data('snippet-editor')._removeSnippet(); - }); - } - } - - // clean editor if they are image or table in deleted content - $('.note-control-selection').hide(); - $('.o_table_handler').remove(); - - this.trigger_up('snippet_removed'); - this.destroy(); - }, //-------------------------------------------------------------------------- // Handlers @@ -495,7 +494,7 @@ var SnippetEditor = Widget.extend({ _onRemoveClick: function (ev) { ev.preventDefault(); this.trigger_up('request_history_undo_record', {$target: this.$target}); - this._removeSnippet(); + this.removeSnippet(); }, }); @@ -511,6 +510,7 @@ var SnippetsMenu = Widget.extend({ deactivate_snippet: '_onDeactivateSnippet', drag_and_drop_stop: '_onDragAndDropStop', go_to_parent: '_onGoToParent', + remove_snippet: '_onRemoveSnippet', snippet_removed: '_onSnippetRemoved', }, @@ -634,9 +634,7 @@ var SnippetsMenu = Widget.extend({ */ cleanForSave: function () { this.trigger_up('ready_to_clean_for_save'); - _.each(this.snippetEditors, function (snippetEditor) { - snippetEditor.cleanForSave(); - }); + this._destroyEditors(); this.$editable.find('[contentEditable]') .removeAttr('contentEditable') @@ -644,17 +642,6 @@ var SnippetsMenu = Widget.extend({ this.$editable.find('.o_we_selected_image') .removeClass('o_we_selected_image'); - - // Removing query_string used to invalidate cache during image crop - _.each(this.$editable.find('.o_cropped_img_to_clean'), function (croppedImg) { - var $croppedImg = $(croppedImg); - var s = $croppedImg.attr('src').split('?'); - var src = s[0]; - var urlParams = s[1] || ''; - urlParams = $.param(_.omit($.deparam(urlParams), 'unique')); - $croppedImg.attr('src', src + (urlParams ? ('?' + urlParams) : '')); - $croppedImg.removeClass('o_cropped_img_to_clean'); - }); }, //-------------------------------------------------------------------------- @@ -818,6 +805,14 @@ var SnippetsMenu = Widget.extend({ return $.when(); }, /** + * @private + */ + _destroyEditors: function () { + _.each(this.snippetEditors, function (snippetEditor) { + snippetEditor.destroy(); + }); + }, + /** * Updates the cover dimensions of the current snippet editor. * * @private @@ -1355,15 +1350,11 @@ var SnippetsMenu = Widget.extend({ /** * Called when a snippet has moved in the page. * - * @todo technically, as a snippet has been moved, all editors should be - * destroyed as their snippet options may not correspond to their - * selector anymore. However this should rarely (maybe never ?) be the case, - * so we might not want to do this as it would slow the editor. - * * @private * @param {OdooEvent} ev */ _onDragAndDropStop: function (ev) { + this._destroyEditors(); this._activateSnippet(ev.data.$snippet); }, /** @@ -1378,6 +1369,16 @@ var SnippetsMenu = Widget.extend({ this._activateSnippet(ev.data.$snippet.parent()); }, /** + * @private + * @param {OdooEvent} ev + */ + _onRemoveSnippet: function (ev) { + ev.stopPropagation(); + this._createSnippetEditor(ev.data.$snippet).then(function (editor) { + editor.removeSnippet(); + }); + }, + /** * Called when a snippet is removed -> checks if there is draggable snippets * to enable/disable as the DOM changed. * diff --git a/addons/web_editor/static/src/js/editor/transcoder.js b/addons/web_editor/static/src/js/editor/transcoder.js index fb3e69eda29..84fe3cf8c93 100644 --- a/addons/web_editor/static/src/js/editor/transcoder.js +++ b/addons/web_editor/static/src/js/editor/transcoder.js @@ -108,9 +108,14 @@ function getMatchedCSSRules(a) { // The css generates all the attributes separately and not in simplified form. // In order to have a better compatibility (outlook for example) we simplify the css tags. // e.g. border-left-style: none; border-bottom-s .... will be simplified in border-style = none - _.each([['margin'], ['padding'], ['border', 'style']], function (attr) { - var p = attr[0]; - var e = attr[1] ? '-' + attr[1] : ''; + _.each([ + {property: 'margin'}, + {property: 'padding'}, + {property: 'border', propertyEnd: '-style', defaultValue: 'none'}, + ], function (propertyInfo) { + var p = propertyInfo.property; + var e = propertyInfo.propertyEnd || ''; + var defVal = propertyInfo.defaultValue || 0; if (style[p+'-top'+e] || style[p+'-right'+e] || style[p+'-bottom'+e] || style[p+'-left'+e]) { if (style[p+'-top'+e] === style[p+'-right'+e] && style[p+'-top'+e] === style[p+'-bottom'+e] && style[p+'-top'+e] === style[p+'-left'+e]) { @@ -119,7 +124,7 @@ function getMatchedCSSRules(a) { } else { // keep => property: [top value] [right value] [bottom value] [left value]; - style[p+e] = (style[p+'-top'+e] || 0) + ' ' + (style[p+'-right'+e] || 0) + ' ' + (style[p+'-bottom'+e] || 0) + ' ' + (style[p+'-left'+e] || 0); + style[p+e] = (style[p+'-top'+e] || defVal) + ' ' + (style[p+'-right'+e] || defVal) + ' ' + (style[p+'-bottom'+e] || defVal) + ' ' + (style[p+'-left'+e] || defVal); if (style[p+e].indexOf('inherit') !== -1 || style[p+e].indexOf('initial') !== -1) { // keep => property-top: [top value]; property-right: [right value]; property-bottom: [bottom value]; property-left: [left value]; delete style[p+e]; @@ -277,11 +282,12 @@ function classToStyle($editable) { } // Outlook - if (node.nodeName === 'A' && $target.hasClass('btn') && !$target.children().length) { - var $hack = $('<table class="o_outlook_hack"><tr><td></td></tr></table>'); + if (node.nodeName === 'A' && $target.hasClass('btn') && !$target.hasClass('btn-link') && !$target.children().length) { + var $hack = $('<table class="o_outlook_hack" style="display: inline-table;"><tr><td></td></tr></table>'); $hack.find('td') .attr('height', $target.outerHeight()) .css({ + 'text-align': $target.parent().css('text-align'), 'margin': $target.css('padding'), 'border-radius': $target.css('border-radius'), 'background-color': $target.css('background-color'), diff --git a/addons/web_editor/static/src/js/widgets/widgets.js b/addons/web_editor/static/src/js/widgets/widgets.js index 8ff47ba2c5a..e6c15aeb4ff 100644 --- a/addons/web_editor/static/src/js/widgets/widgets.js +++ b/addons/web_editor/static/src/js/widgets/widgets.js @@ -291,6 +291,10 @@ var ImageWidget = MediaWidget.extend({ } var img = this.images[0]; + if (!img) { + return this.media; + } + var def = $.when(); if (!img.access_token) { def = this._rpc({ @@ -363,6 +367,7 @@ var ImageWidget = MediaWidget.extend({ if (needle && needle.length) { domain.push('|', ['datas_fname', 'ilike', needle], ['name', 'ilike', needle]); } + domain.push('!', ['datas_fname', '=like', '%.crop'], '!', ['name', '=like', '%.crop']); return this._rpc({ model: 'ir.attachment', method: 'search_read', @@ -1222,7 +1227,7 @@ var MediaDialog = Dialog.extend({ } return $.when(this.active.save()).then(function (media) { - if (!self.media) { + if (!self.media && media) { self.range.insertNode(media, true); } self.media = media; @@ -1234,7 +1239,7 @@ var MediaDialog = Dialog.extend({ // Update editor bar after image edition (in case the image change to icon or other) _.defer(function () { - if (!self.media.parentNode) { + if (!self.media || !self.media.parentNode) { return; } range.createFromNode(self.media).select(); @@ -1578,23 +1583,21 @@ var CropImageDialog = Dialog.extend({ init: function (parent, options, $editable, media) { this.media = media; this.$media = $(this.media); - var srcSplit = this.$media.attr('src').split('?'); - var src = srcSplit[0]; - this.imageData = { - initialSrc: src, - src: src, // the original src for cropped DB images will be fetched later - srcParams: $.deparam(srcSplit[1] || ''), - mimetype: _.str.endsWith(src, '.png') ? 'image/png' : 'image/jpeg', // the mimetype for DB images will be fetched later - aspectRatio: this.$media.data('aspectRatio') || 0, - isExternalImage: src[0] !== '/' && src.indexOf(window.location.host) < 0, - }; + var src = this.$media.attr('src').split('?')[0]; this.aspectRatioList = [ - [_t("Free"), 0], - ['16:9', 16 / 9], - ['4:3', 4 / 3], - ['1:1', 1], - ['2:3', 2 / 3], + [_t("Free"), '0/0', 0], + ["16:9", '16/9', 16 / 9], + ["4:3", '4/3', 4 / 3], + ["1:1", '1/1', 1], + ["2:3", '2/3', 2 / 3], ]; + this.imageData = { + imageSrc: src, + originalSrc: this.$media.data('crop:originalSrc') || src, // the original src for cropped DB images will be fetched later + mimetype: this.$media.data('crop:mimetype') || (_.str.endsWith(src, '.png') ? 'image/png' : 'image/jpeg'), // the mimetype for DB images will be fetched later + aspectRatio: this.$media.data('aspectRatio') || this.aspectRatioList[0][1], + isExternalImage: src.substr(0, 5) !== 'data:' && src[0] !== '/' && src.indexOf(window.location.host) < 0, + }; this.options = _.extend({ title: _t("Crop Image"), buttons: this.imageData.isExternalImage ? [{ @@ -1624,12 +1627,12 @@ var CropImageDialog = Dialog.extend({ var defs = [def, ajax.loadLibs(this)]; var params = {}; var isDBImage = false; - var matchImageID = this.imageData.src.match(/^\/web\/image\/(\d+)/); + var matchImageID = this.imageData.imageSrc.match(/^\/web\/image\/(\d+)/); if (matchImageID) { params['image_id'] = parseInt(matchImageID[1]); isDBImage = true; } else { - var matchXmlID = this.imageData.src.match(/^\/web\/image\/([^/?]+)/); + var matchXmlID = this.imageData.imageSrc.match(/^\/web\/image\/([^/?]+)/); if (matchXmlID) { params['xml_id'] = matchXmlID[1]; isDBImage = true; @@ -1651,10 +1654,19 @@ var CropImageDialog = Dialog.extend({ start: function () { this.$cropperImage = this.$('.o_cropper_image'); if (this.$cropperImage.length) { + var data = this.$media.data(); + var ratio = 0; + for (var i = 0 ; i < this.aspectRatioList.length ; i++) { + if (this.aspectRatioList[i][1] === data.aspectRatio) { + ratio = this.aspectRatioList[i][2]; + break; + } + } this.$cropperImage.cropper({ viewMode: 1, autoCropArea: 1, - data: _.pick(this.$media.data(), 'aspectRatio', 'x', 'y', 'width', 'height', 'rotate', 'scaleX', 'scaleY') + aspectRatio: ratio, + data: _.pick(data, 'x', 'y', 'width', 'height', 'rotate', 'scaleX', 'scaleY') }); } return this._super.apply(this, arguments); @@ -1669,115 +1681,47 @@ var CropImageDialog = Dialog.extend({ this._super.apply(this, arguments); }, /** - * Stores the cropped image in database. + * Updates the DOM image with cropped data and associates required + * information for a potential future save (where required cropped data + * attachments will be created). * - * @private - * @returns {Deferred} + * @override */ save: function () { - var _super = this._super.bind(this); - var def; - if (this.imageData.initialSrc === this.imageData.src) { - // When this is a new image being cropped - def = this._createImage(); - } else { - // When this is a previously cropped image - def = this._updateImage(); - } - return def.then(_super); - }, + var self = this; + var cropperData = this.$cropperImage.cropper('getData'); - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- + // Mark the media for later creation of required cropped attachments... + this.$media.addClass('o_cropped_img_to_save'); + + // ... and attach required data + this.$media.data('crop:resModel', this.options.res_model); + this.$media.data('crop:resID', this.options.res_id); + this.$media.data('crop:id', this.imageData.id); + this.$media.data('crop:mimetype', this.imageData.mimetype); + this.$media.data('crop:originalSrc', this.imageData.originalSrc); + + // Mark the media with the cropping information which is required for + // a future crop edition + this.$media + .attr('data-aspect-ratio', this.imageData.aspectRatio) + .data('aspectRatio', this.imageData.aspectRatio); + _.each(cropperData, function (value, key) { + key = _.str.dasherize(key); + self.$media.attr('data-' + key, value); + self.$media.data(key, value); + }); - /** - * Gets the base64 data from the cropper canvas. - * - * @private - * @returns {string} base64 image data - */ - _getImageBase64: function () { - var cropperData = this.$cropperImage.cropper('getData'); + // Update the media with base64 source for preview before saving var canvas = this.$cropperImage.cropper('getCroppedCanvas', { width: cropperData.width, height: cropperData.height, }); - return canvas.toDataURL(this.imageData.mimetype).split(',')[1]; - }, - /** - * Creates a new attachment for the cropped image data and updates the image - * with the correct URL. - * - * @private - * @returns {Deferred} - */ - _createImage: function () { - var self = this; - var name = this.imageData.src + '.crop'; - return this._rpc({ - model: 'ir.attachment', - method: 'create', - args: [{ - res_model: this.options.res_model, - res_id: this.options.res_id, - name: name, - datas_fname: name, - datas: this._getImageBase64(), - mimetype: this.imageData.mimetype, - url: this.imageData.src, // To save the original image that was cropped - }], - }).then(function (attachmentID) { - return self._rpc({ - model: 'ir.attachment', - method: 'generate_access_token', - args: [[attachmentID]], - }).then(function (access_token) { - var params = _.omit(self.imageData.srcParams, 'unique'); - params.access_token = access_token[0]; - self.$media.attr('src', '/web/image/' + attachmentID + '?' + $.param(params)); - self._setCropBoxData(); - }); - }); - }, - /** - * Writes the cropped image attachment with the new data and forces a - * refresh of the image. - * - * @private - * @returns {Deferred} - */ - _updateImage: function () { - var self = this; - return this._rpc({ - model: 'ir.attachment', - method: 'write', - args: [[this.imageData.id], {datas: this._getImageBase64()}], - }).then(function () { - self.$media.addClass('o_cropped_img_to_clean'); - var params = _.clone(self.imageData.srcParams); - params.unique = new Date().getTime(); - self.$media.attr('src', '/web/image/' + self.imageData.id + '?' + $.param(params)); - self._setCropBoxData(); - }); - }, - /** - * Saves current crop configuration data as image attributes for future - * edition. - * - * @private - */ - _setCropBoxData: function () { - var self = this; - var cropBoxData = _.extend({ - aspectRatio: this.imageData.aspectRatio, // Library's getData does not export aspect ratio - }, this.$cropperImage.cropper('getData')); - _.each(cropBoxData, function (value, key) { - key = _.str.dasherize(key); - self.$media.attr('data-' + key, value); - self.$media.data(key, value); - }); + this.$media.attr('src', canvas.toDataURL(this.imageData.mimetype)); + this.$media.trigger('content_changed'); + + return this._super.apply(this, arguments); }, //-------------------------------------------------------------------------- @@ -1800,8 +1744,8 @@ var CropImageDialog = Dialog.extend({ $option.siblings().removeClass('active'); $option.addClass('active'); this.$cropperImage.cropper('reset'); - this.imageData.aspectRatio = value; - this.$cropperImage.cropper('setAspectRatio', this.imageData.aspectRatio); + this.imageData.aspectRatio = $option.data('label'); + this.$cropperImage.cropper('setAspectRatio', value); break; case 'zoom': case 'rotate': diff --git a/addons/web_editor/static/src/scss/web_editor.ui.scss b/addons/web_editor/static/src/scss/web_editor.ui.scss index 787cb95f0af..179f814c4b3 100644 --- a/addons/web_editor/static/src/scss/web_editor.ui.scss +++ b/addons/web_editor/static/src/scss/web_editor.ui.scss @@ -440,7 +440,7 @@ html[lang] > body.editor_enable [data-oe-translation-state] { > li { position: relative; &:hover { - background-color: fadeOut($o-we-color-dark, 50%); + background-color: fade-out($o-we-color-dark, 0.5); > a { color: white; background: transparent; @@ -468,7 +468,7 @@ html[lang] > body.editor_enable [data-oe-translation-state] { } &.active > a { color: white; - background-color: fadeOut($o-we-color-dark, 50%); + background-color: fade-out($o-we-color-dark, 0.5); &:before { @include o-position-absolute($top: 5px, $left: 5px); content: "\f00c"; @@ -793,7 +793,7 @@ html[lang] > body.editor_enable [data-oe-translation-state] { padding: 4px; border-radius: 2px; border: 1px solid #989898; - border-color: fadeOut($o-we-color-text-normal, 0.5); + border-color: fade-out($o-we-color-text-normal, 0.5); } .note-color-palette .note-color-row { height: auto!important; @@ -1587,7 +1587,7 @@ body .modal { display: block; width: 100%; height: 1px; - background: fadeOut(lighten($o-brand-optional, 30%), 50%); + background: fade-out(lighten($o-brand-optional, 30%), 0.5); pointer-events: none; &.grid-2 { top: 66.66% diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml index e0626801e68..45a16057cd5 100644 --- a/addons/web_editor/static/src/xml/editor.xml +++ b/addons/web_editor/static/src/xml/editor.xml @@ -313,13 +313,13 @@ </div> <t t-else=""> <div class="o_crop_area"> - <img class="img img-responsive o_cropper_image" t-att-src="widget.imageData.src" /> + <img class="img img-responsive o_cropper_image" t-att-src="widget.imageData.originalSrc"/> </div> <div class="o_crop_options text-center mt16"> <div class="btn-group" title="Aspect Ratio" data-toggle="buttons"> <t t-foreach="widget.aspectRatioList" t-as="ratio"> <t t-set="is_active" t-value="ratio[1] === widget.imageData.aspectRatio"/> - <a href="#" t-attf-class="btn btn-default#{is_active and ' active' or ''}" data-event="ratio" t-att-data-value="ratio[1]"> + <a href="#" t-attf-class="btn btn-default#{is_active and ' active' or ''}" data-event="ratio" t-att-data-label="ratio[1]" t-att-data-value="ratio[2]"> <t t-esc="ratio[0]"/> </a> </t> |
