diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/media.js b/addons/web_editor/static/src/js/wysiwyg/plugin/media.js index e6a598a2813..9ba83486e25 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/media.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/media.js @@ -67,8 +67,8 @@ var MediaPlugin = AbstractPlugin.extend({ $(media).clone()[0] ); - mediaDialog.on('saved', this, function (data) { - this.insertMedia(media, data); + mediaDialog.on('save', this, function (element) { + this.insertMedia(media, element); }); mediaDialog.on('closed', this, function () { this.context.invoke('editor.restoreRange'); @@ -157,13 +157,12 @@ var MediaPlugin = AbstractPlugin.extend({ * Insert or replace a media. * * @param {Node} previous the media to replace, if any - * @param {Object} data contains the media to insert + * @param {Node} newMedia contains the media to insert */ - insertMedia: function (previous, data) { - if (!data.media) { + insertMedia: function (previous, newMedia) { + if (!newMedia) { return; } - var newMedia = data.media; this._wrapCommand(function () { this.$editable.focus(); var rng = this.context.invoke('editor.createRange'); @@ -689,17 +688,17 @@ var ImagePlugin = AbstractMediaPlugin.extend({ //-------------------------------------------------------------------------- /** - * Open the crop image dialog and listen to its saved/closed events. + * Open the crop image dialog and listen to its save/closed events. */ cropImageDialog: function () { this.context.invoke('editor.saveRange'); var media = this.context.invoke('editor.restoreTarget'); var cropImageDialog = new weWidgets.CropImageDialog(this.options.parent, {}, - $(media).clone() + $(media).clone()[0] ); - cropImageDialog.on('saved', this, function (data) { - this.context.invoke('MediaPlugin.insertMedia', media, data); + cropImageDialog.on('save', this, function (img) { + this.context.invoke('MediaPlugin.insertMedia', media, img); }); cropImageDialog.on('closed', this, function () { this.context.invoke('editor.restoreRange'); @@ -708,18 +707,18 @@ var ImagePlugin = AbstractMediaPlugin.extend({ cropImageDialog.open(); }, /** - * Open the alt dialog (change image title and alt) and listen to its saved/closed events. + * Open the alt dialog (change image title and alt) and listen to its save/closed events. */ altDialg: function () { this.context.invoke('editor.saveRange'); var media = this.context.invoke('editor.restoreTarget'); var altDialog = new weWidgets.AltDialog(this.options.parent, {}, - $(media).clone() + $(media).clone()[0] ); - altDialog.on('saved', this, this._wrapCommand(function (data) { - $(media).attr('alt', $(data.media).attr('alt')) - .attr('title', $(data.media).attr('title')) + altDialog.on('save', this, this._wrapCommand(function (img) { + $(media).attr('alt', img.alt) + .attr('title', img.title) .trigger('content_changed'); })); altDialog.on('closed', this, function () { diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/alt_dialog.js b/addons/web_editor/static/src/js/wysiwyg/widgets/alt_dialog.js index dd6baff130f..98abc75f6c1 100644 --- a/addons/web_editor/static/src/js/wysiwyg/widgets/alt_dialog.js +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/alt_dialog.js @@ -52,9 +52,7 @@ var AltDialog = Dialog.extend({ $(this.media).attr('alt', alt ? alt.replace(allNonEscQuots, """) : null) .attr('title', title ? title.replace(allNonEscQuots, """) : null); - this.trigger('saved', { - media: this.media, - }); + this.final_data = this.media; return this._super.apply(this, arguments); }, }); diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/crop_dialog.js b/addons/web_editor/static/src/js/wysiwyg/widgets/crop_dialog.js index cb1feaf3f4d..19623d05f5e 100644 --- a/addons/web_editor/static/src/js/wysiwyg/widgets/crop_dialog.js +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/crop_dialog.js @@ -172,9 +172,7 @@ var CropImageDialog = Dialog.extend({ }); this.$media.attr('src', canvas.toDataURL(this.imageData.mimetype)); - this.trigger('saved', { - media: this.$media[0], - }); + this.final_data = this.media; return this._super.apply(this, arguments); }, diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/media.js b/addons/web_editor/static/src/js/wysiwyg/widgets/media.js index ddd987f4ed2..f901e9eb3f1 100644 --- a/addons/web_editor/static/src/js/wysiwyg/widgets/media.js +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/media.js @@ -13,18 +13,17 @@ var _t = core._t; var MediaWidget = Widget.extend({ xmlDependencies: ['/web_editor/static/src/xml/wysiwyg.xml'], - events: { - 'input input.o_we_search': '_onSearchInput', - }, /** * @constructor + * @param {Element} media: the target Element for which we select a media + * @param {Object} options: useful parameters such as res_id, res_model, + * context, user_id, ... */ init: function (parent, media, options) { this._super.apply(this, arguments); this.media = media; this.$media = $(media); - this._onSearchInput = _.debounce(this._onSearchInput, 500); }, //-------------------------------------------------------------------------- @@ -41,14 +40,10 @@ var MediaWidget = Widget.extend({ this._clear(); }, /** + * Saves the currently configured media on the target media. + * * @abstract - * @param {string} needle - * @returns {Deferred} - */ - search: function (needle) {}, - /** - * @abstract - * @returns {*} + * @returns {Promise} */ save: function () {}, @@ -60,6 +55,33 @@ var MediaWidget = Widget.extend({ * @abstract */ _clear: function () {}, +}); + +var SearchableMediaWidget = MediaWidget.extend({ + events: _.extend({}, MediaWidget.prototype.events || {}, { + 'input .o_we_search': '_onSearchInput', + }), + + /** + * @constructor + */ + init: function () { + this._super.apply(this, arguments); + this._onSearchInput = _.debounce(this._onSearchInput, 500); + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * Finds and displays existing attachments related to the target media. + * + * @abstract + * @param {string} needle: only return attachments matching this parameter + * @returns {Promise} + */ + search: function (needle) {}, //-------------------------------------------------------------------------- // Handlers @@ -75,22 +97,23 @@ var MediaWidget = Widget.extend({ }); /** - * Let users choose an image, including uploading a new image in odoo. + * Let users choose a file, including uploading a new file in odoo. */ -var ImageWidget = MediaWidget.extend({ - template: 'wysiwyg.widgets.image', - events: _.extend({}, MediaWidget.prototype.events || {}, { +var FileWidget = SearchableMediaWidget.extend({ + events: _.extend({}, SearchableMediaWidget.prototype.events || {}, { 'click .o_upload_media_button': '_onUploadButtonClick', 'click .o_upload_media_button_no_optimization': '_onUploadButtonNoOptimizationClick', - 'change input[type=file]': '_onImageSelection', + 'change .o_file_input': '_onImageSelection', 'click .o_upload_media_url_button': '_onUploadURLButtonClick', - 'input input[name="url"]': '_onURLInputChange', - 'click .existing-attachments [data-src]': '_onImageClick', - 'dblclick .existing-attachments [data-src]': '_onImageDblClick', + 'input .o_we_url_input': '_onURLInputChange', + 'click .o_existing_attachment_cell [data-src]': '_onImageClick', + 'dblclick .o_existing_attachment_cell [data-src]': '_onImageDblClick', 'click .o_existing_attachment_remove': '_onRemoveClick', 'click .o_load_more': '_onLoadMoreClick', }), + existingAttachmentsTemplate: undefined, + IMAGE_MIMETYPES: ['image/gif', 'image/jpe', 'image/jpeg', 'image/jpg', 'image/gif', 'image/png'], IMAGES_PER_ROW: 6, IMAGES_ROWS: 5, @@ -104,14 +127,10 @@ var ImageWidget = MediaWidget.extend({ this.imagesRows = this.IMAGES_ROWS; this.IMAGES_DISPLAYED_TOTAL = this.IMAGES_PER_ROW * this.imagesRows; - this.options = options; - this.context = options.context; - this.accept = options.accept || (options.document ? '*/*' : 'image/*'); - - this.multiImages = options.multiImages; - - this.firstFilters = options.firstFilters || []; - this.lastFilters = options.lastFilters || []; + this.options = _.extend({ + firstFilters: [], + lastFilters: [], + }, options || {}); this.images = []; }, @@ -130,6 +149,15 @@ var ImageWidget = MediaWidget.extend({ start: function () { var def = this._super.apply(this, arguments); var self = this; + this.$urlInput = this.$('.o_we_url_input'); + this.$form = this.$('form'); + this.$fileInput = this.$('.o_file_input'); + this.$uploadButton = this.$('.o_upload_media_button'); + this.$addUrlButton = this.$('.o_upload_media_url_button'); + this.$urlSuccess = this.$('.o_we_url_success'); + this.$urlWarning = this.$('.o_we_url_warning'); + this.$urlError = this.$('.o_we_url_error'); + this.$errorText = this.$('.o_we_error_text'); this._renderImages(true); @@ -155,6 +183,9 @@ var ImageWidget = MediaWidget.extend({ //-------------------------------------------------------------------------- /** + * Saves the currently selected image on the target media. If new files are + * currently being added, delays the save until all files have been added. + * * @override */ save: function () { @@ -166,7 +197,7 @@ var ImageWidget = MediaWidget.extend({ search: function (needle, noRender) { var self = this; if (!noRender) { - this.$('input.o_we_url_input').val('').trigger('input').trigger('change'); + this.$urlInput.val('').trigger('input').trigger('change'); } return this._rpc({ model: 'ir.attachment', @@ -176,7 +207,7 @@ var ImageWidget = MediaWidget.extend({ domain: this._getAttachmentsDomain(needle), fields: ['name', 'datas_fname', 'mimetype', 'checksum', 'url', 'type', 'res_id', 'res_model', 'access_token'], order: [{name: 'id', asc: false}], - context: this.context, + context: this.options.context, }, }).then(function (records) { self.records = _.chain(records) @@ -187,13 +218,13 @@ var ImageWidget = MediaWidget.extend({ return (r.url || r.id); }) .sortBy(function (r) { - if (_.any(self.firstFilters, function (filter) { + if (_.any(self.options.firstFilters, function (filter) { var regex = new RegExp(filter, 'i'); return r.name.match(regex) || r.datas_fname && r.datas_fname.match(regex); })) { return -1; } - if (_.any(self.lastFilters, function (filter) { + if (_.any(self.options.lastFilters, function (filter) { var regex = new RegExp(filter, 'i'); return r.name.match(regex) || r.datas_fname && r.datas_fname.match(regex); })) { @@ -277,10 +308,7 @@ var ImageWidget = MediaWidget.extend({ domain = domain.concat(attachedDocumentDomain); } domain = ['|', ['public', '=', true]].concat(domain); - - domain.push('|', - ['mimetype', '=', false], - ['mimetype', this.options.document ? 'not in' : 'in', ['image/gif', 'image/jpe', 'image/jpeg', 'image/jpg', 'image/gif', 'image/png']]); + domain = domain.concat(this.options.mimetypeDomain); if (needle && needle.length) { domain.push('|', ['datas_fname', 'ilike', needle], ['name', 'ilike', needle]); } @@ -315,6 +343,20 @@ var ImageWidget = MediaWidget.extend({ this.search(this.$('.o_we_search').val() || ''); } }, + /** + * Renders the existing attachments and returns the result as a string. + * + * @param {Object[]} rows + * @param {boolean} withEffect + * @returns {string} + */ + _renderExisting: function (rows, withEffect) { + return QWeb.render(this.existingAttachmentsTemplate, { + rows: rows, + widget: this, + withEffect: withEffect, + }); + }, /** * @private */ @@ -326,15 +368,11 @@ var ImageWidget = MediaWidget.extend({ .values() .value(); - this.$('.form-text').empty(); + this.$errorText.empty(); // Render menu & content this.$('.existing-attachments').replaceWith( - QWeb.render('wysiwyg.widgets.files.existing.content', { - rows: rows, - isDocument: this.options.document, - withEffect: withEffect, - }) + this._renderExisting(rows, withEffect) ); var $divs = this.$('.o_image'); @@ -363,16 +401,18 @@ var ImageWidget = MediaWidget.extend({ }, /** * @private + * @returns {Promise} */ _save: function () { var self = this; - if (this.multiImages) { - return this.images; + + if (this.options.multiImages) { + return Promise.resolve(this.images); } var img = this.images[0]; if (!img) { - return this.media; + return Promise.resolve(this.media); } var prom; @@ -421,11 +461,6 @@ var ImageWidget = MediaWidget.extend({ self.$media.css(style); } - if (self.options.onUpload) { - // We consider that when selecting an image it is as if we upload it in the html content. - self.options.onUpload([img]); - } - // Remove crop related attributes if (self.$media.attr('data-aspect-ratio')) { var attrs = ['aspect-ratio', 'x', 'y', 'width', 'height', 'rotate', 'scale-x', 'scale-y']; @@ -441,7 +476,7 @@ var ImageWidget = MediaWidget.extend({ * @private */ _toggleImage: function (attachment, clearSearch, forceSelect) { - if (this.multiImages) { + if (this.options.multiImages) { var img = _.select(this.images, function (v) { return v.id === attachment.id; }); if (img.length) { if (!forceSelect) { @@ -459,6 +494,22 @@ var ImageWidget = MediaWidget.extend({ this.search(''); } }, + /** + * Updates the add by URL UI. + * + * @private + * @param {boolean} emptyValue + * @param {boolean} isURL + * @param {boolean} isImage + */ + _updateAddUrlUi(emptyValue, isURL, isImage) { + this.$addUrlButton.toggleClass('btn-secondary', emptyValue) + .toggleClass('btn-primary', !emptyValue) + .prop('disabled', !isURL); + + this.$urlSuccess.toggleClass('d-none', !isURL); + this.$urlError.toggleClass('d-none', emptyValue || isURL); + }, /** * @private */ @@ -504,24 +555,24 @@ var ImageWidget = MediaWidget.extend({ resolve(); function _processFile(attachment, error) { - var $button = self.$('.o_upload_image_button'); + var $button = self.$uploadButton; if (!error) { $button.addClass('btn-success'); self._toggleImage(attachment, true); } else { $button.addClass('btn-danger'); self.$el.addClass('o_has_error').find('.form-control, .custom-select').addClass('is-invalid'); - self.$el.find('.form-text').text(error); + self.$errorText.text(error); } - if (!self.multiImages) { + if (!self.options.multiImages) { self.trigger_up('save_request'); } } }); self.$el.submit(); - self.$('.o_file_input').val(''); + self.$fileInput.val(''); }); }, @@ -551,11 +602,10 @@ var ImageWidget = MediaWidget.extend({ * @private */ _onImageSelection: function () { - var $form = this.$('form'); this.$el.addClass('nosave'); - $form.removeClass('o_has_error').find('.form-control, .custom-select').removeClass('is-invalid'); - $form.find('.form-text').empty(); - this.$('.o_upload_media_button').removeClass('btn-danger btn-success'); + this.$form.removeClass('o_has_error').find('.form-control, .custom-select').removeClass('is-invalid'); + this.$errorText.empty(); + this.$uploadButton.removeClass('btn-danger btn-success'); this._uploadFile(); }, /** @@ -565,7 +615,7 @@ var ImageWidget = MediaWidget.extend({ var self = this; Dialog.confirm(this, _t("Are you sure you want to delete this file ?"), { confirm_callback: function () { - var $helpBlock = self.$('.form-text').empty(); + var $helpBlock = self.$errorText.empty(); var $a = $(ev.currentTarget); var id = parseInt($a.data('id'), 10); var attachment = _.findWhere(self.records, {id: id}); @@ -582,6 +632,7 @@ var ImageWidget = MediaWidget.extend({ } $helpBlock.replaceWith(QWeb.render('wysiwyg.widgets.image.existing.error', { views: prevented[id], + widget: self, })); }); } @@ -592,10 +643,6 @@ var ImageWidget = MediaWidget.extend({ */ _onURLInputChange: function (ev) { var $input = $(ev.currentTarget); - var $button = this.$('.o_upload_media_url_button'); - var $success = this.$('.o_we_url_success'); - var $warning = this.$('.o_we_url_warning'); - var $error = this.$('.o_we_url_error'); var inputValue = $input.val(); var emptyValue = (inputValue === ''); @@ -605,27 +652,20 @@ var ImageWidget = MediaWidget.extend({ return inputValue.endsWith(format); }); - $button.toggleClass('btn-secondary', emptyValue).toggleClass('btn-primary', !emptyValue) - .prop('disabled', !isURL); - if (!this.options.document) { - $button.text((isURL && !isImage) ? _t("Add as document") : _t("Add image")); - } - $success.toggleClass('d-none', !isURL); - $warning.toggleClass('d-none', !isURL || this.options.document || isImage); - $error.toggleClass('d-none', emptyValue || isURL); + this._updateAddUrlUi(emptyValue, isURL, isImage); }, /** * @private */ _onUploadButtonClick: function () { - this.$('input[type=file]').click(); + this.$fileInput.click(); }, /** * @private */ _onUploadButtonNoOptimizationClick: function () { this.$('input[name="disable_optimization"]').val('1'); - this.$('.o_upload_media_button').click(); + this.$uploadButton.click(); }, /** * @private @@ -649,13 +689,78 @@ var ImageWidget = MediaWidget.extend({ }, }); +/** + * Let users choose an image, including uploading a new image in odoo. + */ +var ImageWidget = FileWidget.extend({ + template: 'wysiwyg.widgets.image', + existingAttachmentsTemplate: 'wysiwyg.widgets.image.existing.attachments', + + /** + * @constructor + */ + init: function (parent, media, options) { + options = _.extend({ + accept: 'image/*', + mimetypeDomain: [['mimetype', 'in', this.IMAGE_MIMETYPES]], + }, options || {}); + this._super(parent, media, options); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _updateAddUrlUi: function (emptyValue, isURL, isImage) { + this._super.apply(this, arguments); + this.$addUrlButton.text((isURL && !isImage) ? _t("Add as document") : _t("Add image")); + this.$urlWarning.toggleClass('d-none', !isURL || isImage); + }, +}); + + +/** + * Let users choose a document, including uploading a new document in odoo. + */ +var DocumentWidget = FileWidget.extend({ + template: 'wysiwyg.widgets.document', + existingAttachmentsTemplate: 'wysiwyg.widgets.document.existing.attachments', + + /** + * @constructor + */ + init: function (parent, media, options) { + options = _.extend({ + accept: '*/*', + mimetypeDomain: [['mimetype', 'not in', this.IMAGE_MIMETYPES]], + }, options || {}); + this._super(parent, media, options); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _updateAddUrlUi: function (emptyValue, isURL, isImage) { + this._super.apply(this, arguments); + this.$addUrlButton.text((isURL && isImage) ? _t("Add as image") : _t("Add document")); + this.$urlWarning.toggleClass('d-none', !isURL || !isImage); + }, +}); + /** * Let users choose a font awesome icon, support all font awesome loaded in the * css files. */ -var IconWidget = MediaWidget.extend({ +var IconWidget = SearchableMediaWidget.extend({ template: 'wysiwyg.widgets.font-icons', - events: _.extend({}, MediaWidget.prototype.events || {}, { + events: _.extend({}, SearchableMediaWidget.prototype.events || {}, { 'click .font-icons-icon': '_onIconClick', 'dblclick .font-icons-icon': '_onIconDblClick', }), @@ -712,7 +817,7 @@ var IconWidget = MediaWidget.extend({ class: _.compact(finalClasses).join(' '), style: style || null, }); - return this.media; + return Promise.resolve(this.media); }, /** * @override @@ -736,7 +841,7 @@ var IconWidget = MediaWidget.extend({ }); } this.$('div.font-icons-icons').html( - QWeb.render('wysiwyg.widgets.font-icons.icons', {iconsParser: iconsParser}) + QWeb.render('wysiwyg.widgets.font-icons.icons', {iconsParser: iconsParser, widget: this}) ); return Promise.resolve(); }, @@ -870,7 +975,7 @@ var VideoWidget = MediaWidget.extend({ ); this.media = this.$media[0]; } - return this.media; + return Promise.resolve(this.media); }, //-------------------------------------------------------------------------- @@ -1095,7 +1200,10 @@ var VideoWidget = MediaWidget.extend({ return { MediaWidget: MediaWidget, + SearchableMediaWidget: SearchableMediaWidget, + FileWidget: FileWidget, ImageWidget: ImageWidget, + DocumentWidget: DocumentWidget, IconWidget: IconWidget, VideoWidget: VideoWidget, }; diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/media_dialog.js b/addons/web_editor/static/src/js/wysiwyg/widgets/media_dialog.js index cdfde911597..92c2f53099e 100644 --- a/addons/web_editor/static/src/js/wysiwyg/widgets/media_dialog.js +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/media_dialog.js @@ -8,9 +8,12 @@ var Dialog = require('wysiwyg.widgets.Dialog'); var _t = core._t; /** - * MediaDialog widget. Lets users change a media, including uploading a - * new image, font awsome or video and can change a media into an other - * media. + * Lets the user select a media. The media can be existing or newly uploaded. + * + * The media can be one of the following types: image, document, video or + * font awesome icon (only existing icons). + * + * The user may change a media into another one depending on the given options. */ var MediaDialog = Dialog.extend({ template: 'wysiwyg.widgets.media', @@ -18,7 +21,10 @@ var MediaDialog = Dialog.extend({ ['/web_editor/static/src/xml/wysiwyg.xml'] ), events: _.extend({}, Dialog.prototype.events, { - 'click a[data-toggle="tab"]': '_onTabChange', + 'click #editor-media-image-tab': '_onClickImageTab', + 'click #editor-media-document-tab': '_onClickDocumentTab', + 'click #editor-media-icon-tab': '_onClickIconTab', + 'click #editor-media-video-tab': '_onClickVideoTab', }), custom_events: _.extend({}, Dialog.prototype.custom_events || {}, { save_request: '_onSaveRequest', @@ -26,10 +32,15 @@ var MediaDialog = Dialog.extend({ /** * @constructor + * @param {Element} media */ init: function (parent, options, media) { - var self = this; - options = options || {}; + var $media = $(media); + + options = _.extend({}, options); + options.noDocuments = options.onlyImages || options.noDocuments; + options.noIcons = options.onlyImages || options.noIcons; + options.noVideos = options.onlyImages || options.noVideos; this._super(parent, _.extend({}, { title: _t("Select a Media"), @@ -44,88 +55,68 @@ var MediaDialog = Dialog.extend({ }, }); - this.media = media; - this.$media = $(media); - - this.multiImages = options.multiImages; - var onlyImages = options.onlyImages || this.multiImages; - this.noImages = options.noImages; - this.noDocuments = onlyImages || options.noDocuments; - this.noIcons = onlyImages || options.noIcons; - this.noVideos = onlyImages || options.noVideos; - - if (!this.noDocuments) { - this.documentDialog = new MediaModules.ImageWidget(this, this.media, _.extend({}, options, { - document: true, - })); - this.documentDialog.tabToShow = 'document'; + if (!options.noImages) { + this.imageWidget = new MediaModules.ImageWidget(this, media, options); } - if (!this.noIcons) { - this.iconDialog = new MediaModules.IconWidget(this, this.media, options); - this.iconDialog.tabToShow = 'icon'; + if (!options.noDocuments) { + this.documentWidget = new MediaModules.DocumentWidget(this, media, options); } - if (!this.noVideos) { - this.videoDialog = new MediaModules.VideoWidget(this, this.media, options); - this.videoDialog.tabToShow = 'video'; + if (!options.noIcons) { + this.iconWidget = new MediaModules.IconWidget(this, media, options); } - if (!this.noImages) { - this.imageDialog = new MediaModules.ImageWidget(this, this.media, options); - this.imageDialog.tabToShow = 'image'; + if (!options.noVideos) { + this.videoWidget = new MediaModules.VideoWidget(this, media, options); } - this.active = this.imageDialog || this.documentDialog || this.iconDialog || this.videoDialog; - if (this.imageDialog && this.$media.is('img')) { - this.active = this.imageDialog; - } else if (this.documentDialog && this.$media.is('a.o_image')) { - this.active = this.documentDialog; - } else if (this.videoDialog && this.$media.hasClass('media_iframe_video')) { - this.active = this.videoDialog; - } else if (this.iconDialog && this.$media.is('span, i')) { - this.active = this.iconDialog; + if (this.imageWidget && $media.is('img')) { + this.activeWidget = this.imageWidget; + } else if (this.documentWidget && $media.is('a.o_image')) { + this.activeWidget = this.documentWidget; + } else if (this.videoWidget && $media.hasClass('media_iframe_video')) { + this.activeWidget = this.videoWidget; + } else if (this.iconWidget && $media.is('span, i')) { + this.activeWidget = this.iconWidget; + } else if (this.imageWidget) { + this.activeWidget = this.imageWidget; } - - this.opened(function () { - self.$('[href="#editor-media-' + self.active.tabToShow + '"]').tab('show'); - }); }, /** + * Adds the appropriate class to the current modal and appends the media + * widgets to their respective tabs. + * * @override */ start: function () { - var self = this; - var defs = [this._super.apply(this, arguments)]; - this.$modal.addClass('note-image-dialog'); + var promises = [this._super.apply(this, arguments)]; this.$modal.find('.modal-dialog').addClass('o_select_media_dialog'); - if (this.imageDialog) { - this.imageDialog.clear(); + if (this.imageWidget) { + this.imageWidget.clear(); } - if (this.documentDialog) { - this.documentDialog.clear(); + if (this.documentWidget) { + this.documentWidget.clear(); } - if (this.iconDialog) { - this.iconDialog.clear(); + if (this.iconWidget) { + this.iconWidget.clear(); } - if (this.videoDialog) { - this.videoDialog.clear(); + if (this.videoWidget) { + this.videoWidget.clear(); } - if (this.imageDialog) { - defs.push(this.imageDialog.appendTo(this.$("#editor-media-image"))); + if (this.imageWidget) { + promises.push(this.imageWidget.appendTo(this.$("#editor-media-image"))); } - if (this.documentDialog) { - defs.push(this.documentDialog.appendTo(this.$("#editor-media-document"))); + if (this.documentWidget) { + promises.push(this.documentWidget.appendTo(this.$("#editor-media-document"))); } - if (this.iconDialog) { - defs.push(this.iconDialog.appendTo(this.$("#editor-media-icon"))); + if (this.iconWidget) { + promises.push(this.iconWidget.appendTo(this.$("#editor-media-icon"))); } - if (this.videoDialog) { - defs.push(this.videoDialog.appendTo(this.$("#editor-media-video"))); + if (this.videoWidget) { + promises.push(this.videoWidget.appendTo(this.$("#editor-media-video"))); } - return Promise.all(defs).then(function () { - self._setActive(self.active); - }); + return Promise.all(promises); }, //-------------------------------------------------------------------------- @@ -133,45 +124,97 @@ var MediaDialog = Dialog.extend({ //-------------------------------------------------------------------------- /** + * Returns whether the document widget is currently active. + * + * @returns {boolean} + */ + isDocumentActive: function () { + return this.activeWidget === this.documentWidget; + }, + /** + * Returns whether the icon widget is currently active. + * + * @returns {boolean} + */ + isIconActive: function () { + return this.activeWidget === this.iconWidget; + }, + /** + * Returns whether the image widget is currently active. + * + * @returns {boolean} + */ + isImageActive: function () { + return this.activeWidget === this.imageWidget; + }, + /** + * Returns whether the video widget is currently active. + * + * @returns {boolean} + */ + isVideoActive: function () { + return this.activeWidget === this.videoWidget; + }, + /** + * Saves the currently selected media from the currently active widget. + * + * The save event data `final_data` will be one Element in general, but it + * will be an Array of Element if `multiImages` is set. + * * @override */ save: function () { var self = this; var _super = this._super; var args = arguments; - return Promise.resolve(this.active.save()).then(function (data) { + return this.activeWidget.save().then(function (data) { self.final_data = data; - // In the case of multi images selection we suppose this was not to - // replace an old media, so we only retrieve the images and save. - if (!self.multiImages) { - // TODO this dialog triggers 'save' and 'saved' with different - // data on close... should refactor to avoid confusion... - self.trigger('saved', { - attachments: self.active.images, - media: data, - }); - } return _super.apply(self, args); }); }, - //-------------------------------------------------------------------------- - // - //-------------------------------------------------------------------------- - - /** - * @private - * @param {Object} widget - */ - _setActive: function (widget) { - this.active = widget; - }, - //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- /** + * Sets the document widget as the active widget. + * + * @private + */ + _onClickDocumentTab: function () { + this.activeWidget = this.documentWidget; + }, + /** + * Sets the icon widget as the active widget. + * + * @private + */ + _onClickIconTab: function () { + this.activeWidget = this.iconWidget; + }, + /** + * Sets the image widget as the active widget. + * + * @private + */ + _onClickImageTab: function () { + this.activeWidget = this.imageWidget; + }, + /** + * Sets the video widget as the active widget. + * + * @private + */ + _onClickVideoTab: function () { + this.activeWidget = this.videoWidget; + }, + /** + * Handles save request from the child widgets. + * + * This is for usability, to allow the user to save from other ways than + * click on the modal button, such as double clicking a media to select it. + * * @private * @param {OdooEvent} ev */ @@ -179,22 +222,6 @@ var MediaDialog = Dialog.extend({ ev.stopPropagation(); this.save(); }, - /** - * @private - * @param {JQueryEvent} ev - */ - _onTabChange: function (ev) { - var $target = $(ev.target); - if ($target.is('[href="#editor-media-image"]')) { - this._setActive(this.imageDialog); - } else if ($target.is('[href="#editor-media-document"]')) { - this._setActive(this.documentDialog); - } else if ($target.is('[href="#editor-media-icon"]')) { - this._setActive(this.active = this.iconDialog); - } else if ($target.is('[href="#editor-media-video"]')) { - this._setActive(this.active = this.videoDialog); - } - }, }); return MediaDialog; diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/widgets.js b/addons/web_editor/static/src/js/wysiwyg/widgets/widgets.js index 18b88951c70..cb17523ac19 100644 --- a/addons/web_editor/static/src/js/wysiwyg/widgets/widgets.js +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/widgets.js @@ -19,7 +19,10 @@ return { ColorpickerDialog: ColorpickerDialog, MediaWidget: media.MediaWidget, + SearchableMediaWidget: media.SearchableMediaWidget, + FileWidget: media.FileWidget, ImageWidget: media.ImageWidget, + DocumentWidget: media.DocumentWidget, IconWidget: media.IconWidget, VideoWidget: media.VideoWidget, }; diff --git a/addons/web_editor/static/src/js/wysiwyg_snippets/snippets.options.js b/addons/web_editor/static/src/js/wysiwyg_snippets/snippets.options.js index 5c7a41ae8f3..020a91b0a2b 100644 --- a/addons/web_editor/static/src/js/wysiwyg_snippets/snippets.options.js +++ b/addons/web_editor/static/src/js/wysiwyg_snippets/snippets.options.js @@ -858,8 +858,8 @@ registry.background = SnippetOption.extend({ res_id: $editable.data('oe-id'), }, $image[0]).open(); - _editor.on('save', this, function () { - this._setCustomBackground($image.attr('src')); + _editor.on('save', this, function (image) { + this._setCustomBackground(image.src); this.$target.trigger('content_changed'); }); _editor.on('closed', this, function () { diff --git a/addons/web_editor/static/src/xml/wysiwyg.xml b/addons/web_editor/static/src/xml/wysiwyg.xml index cd20ee3f4ef..fd4ad33e54d 100644 --- a/addons/web_editor/static/src/xml/wysiwyg.xml +++ b/addons/web_editor/static/src/xml/wysiwyg.xml @@ -68,17 +68,17 @@