diff options
Diffstat (limited to 'addons/web_editor/static')
| -rw-r--r-- | addons/web_editor/static/src/js/wysiwyg/widgets/media.js | 181 | ||||
| -rw-r--r-- | addons/web_editor/static/src/scss/wysiwyg.scss | 53 | ||||
| -rw-r--r-- | addons/web_editor/static/src/xml/wysiwyg.xml | 47 | ||||
| -rw-r--r-- | addons/web_editor/static/tests/field_html_tests.js | 5 | ||||
| -rw-r--r-- | addons/web_editor/static/tests/wysiwyg_snippets_tests.js | 8 | ||||
| -rw-r--r-- | addons/web_editor/static/tests/wysiwyg_tests.js | 3 |
6 files changed, 148 insertions, 149 deletions
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 f901e9eb3f1..8e923acc6db 100644 --- a/addons/web_editor/static/src/js/wysiwyg/widgets/media.js +++ b/addons/web_editor/static/src/js/wysiwyg/widgets/media.js @@ -103,19 +103,18 @@ var FileWidget = SearchableMediaWidget.extend({ events: _.extend({}, SearchableMediaWidget.prototype.events || {}, { 'click .o_upload_media_button': '_onUploadButtonClick', 'click .o_upload_media_button_no_optimization': '_onUploadButtonNoOptimizationClick', - 'change .o_file_input': '_onImageSelection', + 'change .o_file_input': '_onFileInputChange', 'click .o_upload_media_url_button': '_onUploadURLButtonClick', '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_cell': '_onAttachmentClick', + 'dblclick .o_existing_attachment_cell': '_onAttachmentDblClick', '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, + NUMBER_OF_ATTACHMENTS_TO_DISPLAY: 30, /** * @constructor @@ -124,15 +123,15 @@ var FileWidget = SearchableMediaWidget.extend({ this._super.apply(this, arguments); this._mutex = new concurrency.Mutex(); - this.imagesRows = this.IMAGES_ROWS; - this.IMAGES_DISPLAYED_TOTAL = this.IMAGES_PER_ROW * this.imagesRows; + this.numberOfAttachmentsToDisplay = this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY; this.options = _.extend({ firstFilters: [], lastFilters: [], }, options || {}); - this.images = []; + this.attachments = []; + this.selectedAttachments = []; }, /** * @override @@ -159,8 +158,10 @@ var FileWidget = SearchableMediaWidget.extend({ this.$urlError = this.$('.o_we_url_error'); this.$errorText = this.$('.o_we_error_text'); - this._renderImages(true); + this._renderImages(); + // If there is already an attachment on the target, select by default + // that attachment if it is among the loaded images. var o = { url: null, alt: null, @@ -172,7 +173,9 @@ var FileWidget = SearchableMediaWidget.extend({ o.id = +o.url.match(/\/web\/content\/(\d+)/, '')[1]; } if (o.url) { - self._toggleImage(_.find(self.records, function (record) { return record.url === o.url;}) || o, true); + self._toggleImage(_.find(self.attachments, function (attachment) { + return attachment.url === o.url; + }) || o, true); } return def; @@ -193,12 +196,11 @@ var FileWidget = SearchableMediaWidget.extend({ }, /** * @override + * @param {boolean} noRender: if true, do not render the found attachments */ search: function (needle, noRender) { var self = this; - if (!noRender) { - this.$urlInput.val('').trigger('input').trigger('change'); - } + return this._rpc({ model: 'ir.attachment', method: 'search_read', @@ -209,8 +211,8 @@ var FileWidget = SearchableMediaWidget.extend({ order: [{name: 'id', asc: false}], context: this.options.context, }, - }).then(function (records) { - self.records = _.chain(records) + }).then(function (attachments) { + self.attachments = _.chain(attachments) .filter(function (r) { return (r.type === "binary" || r.url && r.url.length > 0); }) @@ -234,13 +236,13 @@ var FileWidget = SearchableMediaWidget.extend({ }) .value(); - _.each(self.records, function (record) { - record.src = record.url || _.str.sprintf('/web/image/%s/%s', record.id, encodeURI(record.name)); // Name is added for SEO purposes - record.isDocument = !(/gif|jpe|jpg|png/.test(record.mimetype)); + _.each(self.attachments, function (attachment) { + // Name is added for SEO purposes + attachment.src = attachment.url || _.str.sprintf('/web/image/%s/%s', attachment.id, encodeURI(attachment.name)); + attachment.isDocument = !(/gif|jpe|jpg|png/.test(attachment.mimetype)); }); if (!noRender) { self._renderImages(); - self._adaptLoadMore(); } }); }, @@ -250,14 +252,6 @@ var FileWidget = SearchableMediaWidget.extend({ //-------------------------------------------------------------------------- /** - * @private - */ - _adaptLoadMore: function () { - var noMoreImgToLoad = this.IMAGES_DISPLAYED_TOTAL >= this.records.length; - this.$('.o_load_more').toggleClass('d-none', noMoreImgToLoad); - this.$('.o_load_done_msg').toggleClass('d-none', !noMoreImgToLoad); - }, - /** * @override */ _clear: function () { @@ -318,27 +312,20 @@ var FileWidget = SearchableMediaWidget.extend({ /** * @private */ - _highlightSelectedImages: function () { + _highlightSelected: function () { var self = this; - this.$('.o_existing_attachment_cell.o_selected').removeClass("o_selected"); - var $select = this.$('.o_existing_attachment_cell [data-src]').filter(function () { - var $img = $(this); - 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")); - }); + this.$('.o_existing_attachment_cell.o_we_attachment_selected').removeClass("o_we_attachment_selected"); + _.each(this.selectedAttachments, function (attachment) { + self.$('.o_existing_attachment_cell[data-id=' + attachment.id + ']').addClass("o_we_attachment_selected"); }); - $select.closest('.o_existing_attachment_cell').addClass("o_selected"); - return $select; }, /** * @private */ _loadMoreImages: function (forceSearch) { - this.imagesRows += 2; - this.IMAGES_DISPLAYED_TOTAL = this.imagesRows * this.IMAGES_PER_ROW; + this.numberOfAttachmentsToDisplay += 10; if (!forceSearch) { this._renderImages(); - this._adaptLoadMore(); } else { this.search(this.$('.o_we_search').val() || ''); } @@ -346,58 +333,34 @@ var FileWidget = SearchableMediaWidget.extend({ /** * Renders the existing attachments and returns the result as a string. * - * @param {Object[]} rows - * @param {boolean} withEffect + * @param {Object[]} attachments * @returns {string} */ - _renderExisting: function (rows, withEffect) { + _renderExisting: function (attachments) { return QWeb.render(this.existingAttachmentsTemplate, { - rows: rows, + attachments: attachments, widget: this, - withEffect: withEffect, }); }, /** * @private */ - _renderImages: function (withEffect) { - var self = this; - var rows = _(this.records).chain() - .slice(0, this.IMAGES_DISPLAYED_TOTAL) - .groupBy(function (a, index) { return Math.floor(index / self.IMAGES_PER_ROW); }) - .values() - .value(); + _renderImages: function () { + var attachments = this.attachments.slice(0, this.numberOfAttachmentsToDisplay); this.$errorText.empty(); - // Render menu & content + // Render menu & content this.$('.existing-attachments').replaceWith( - this._renderExisting(rows, withEffect) + this._renderExisting(attachments) ); - var $divs = this.$('.o_image'); - var imageDefs = _.map($divs, function (el) { - var $div = $(el); - if (/gif|jpe|jpg|png/.test($div.data('mimetype'))) { - var $img = $('<img/>', { - class: 'img-fluid', - src: $div.data('url') || $div.data('src'), - }); - var prom = new Promise(function (resolve, reject) { - $img[0].onload = resolve(); - $div.addClass('o_webimage').append($img); - }); - return prom; - } - }); - if (withEffect) { - Promise.all(imageDefs).then(function () { - _.delay(function () { - $divs.removeClass('o_image_loading'); - }, 400); - }); - } - this._highlightSelectedImages(); + this._highlightSelected(); + + // adapt load more + var noMoreImgToLoad = this.numberOfAttachmentsToDisplay >= this.attachments.length; + this.$('.o_load_more').toggleClass('d-none', noMoreImgToLoad); + this.$('.o_load_done_msg').toggleClass('d-none', !noMoreImgToLoad); }, /** * @private @@ -407,10 +370,10 @@ var FileWidget = SearchableMediaWidget.extend({ var self = this; if (this.options.multiImages) { - return Promise.resolve(this.images); + return Promise.resolve(this.selectedAttachments); } - var img = this.images[0]; + var img = this.selectedAttachments[0]; if (!img) { return Promise.resolve(this.media); } @@ -475,24 +438,24 @@ var FileWidget = SearchableMediaWidget.extend({ /** * @private */ - _toggleImage: function (attachment, clearSearch, forceSelect) { + _toggleImage: function (attachment, doubleClick) { if (this.options.multiImages) { - var img = _.select(this.images, function (v) { return v.id === attachment.id; }); - if (img.length) { - if (!forceSelect) { - this.images.splice(this.images.indexOf(img[0]),1); + // if the clicked image is already selected, then unselect it + // unless it was a double click + var index = this.selectedAttachments.indexOf(attachment); + if (index !== -1) { + if (!doubleClick) { + this.selectedAttachments.splice(index, 1); } } else { - this.images.push(attachment); + // if the clicked image is not selected, then select it + this.selectedAttachments.push(attachment); } } else { - this.images = [attachment]; - } - this._highlightSelectedImages(); - - if (clearSearch) { - this.search(''); + // select the clicked image + this.selectedAttachments = [attachment]; } + this._highlightSelected(); }, /** * Updates the add by URL UI. @@ -536,14 +499,15 @@ var FileWidget = SearchableMediaWidget.extend({ self.$('.well > span').remove(); self.$('.well > div').show(); - _.each(attachments, function (record) { - record.src = record.url || _.str.sprintf('/web/image/%s/%s', record.id, encodeURI(record.name)); // Name is added for SEO purposes - record.isDocument = !(/gif|jpe|jpg|png/.test(record.mimetype)); + _.each(attachments, function (attachment) { + // Name is added for SEO purposes + attachment.src = attachment.url || _.str.sprintf('/web/image/%s/%s', attachment.id, encodeURI(attachment.name)); + attachment.isDocument = !(/gif|jpe|jpg|png/.test(attachment.mimetype)); }); if (error || !attachments.length) { _processFile(null, error || !attachments.length); } - self.images = attachments; + self.attachments = attachments; for (var i = 0 ; i < attachments.length ; i++) { _processFile(attachments[i], error); } @@ -584,24 +548,22 @@ var FileWidget = SearchableMediaWidget.extend({ /** * @private */ - _onImageClick: function (ev, force_select) { - var $img = $(ev.currentTarget); - var attachment = _.find(this.records, function (record) { - return record.id === $img.data('id'); - }); - this._toggleImage(attachment, false, force_select); + _onAttachmentClick: function (ev, doubleClick) { + var $attachment = $(ev.currentTarget); + var attachment = _.find(this.attachments, {id: $attachment.data('id')}); + this._toggleImage(attachment, doubleClick); }, /** * @private */ - _onImageDblClick: function (ev) { - this._onImageClick(ev, true); + _onAttachmentDblClick: function (ev) { + this._onAttachmentClick(ev, true); this.trigger_up('save_request'); }, /** * @private */ - _onImageSelection: function () { + _onFileInputChange: function () { this.$el.addClass('nosave'); this.$form.removeClass('o_has_error').find('.form-control, .custom-select').removeClass('is-invalid'); this.$errorText.empty(); @@ -616,9 +578,9 @@ var FileWidget = SearchableMediaWidget.extend({ Dialog.confirm(this, _t("Are you sure you want to delete this file ?"), { confirm_callback: function () { var $helpBlock = self.$errorText.empty(); - var $a = $(ev.currentTarget); + var $a = $(ev.currentTarget).closest('.o_existing_attachment_cell'); var id = parseInt($a.data('id'), 10); - var attachment = _.findWhere(self.records, {id: id}); + var attachment = _.findWhere(self.attachments, {id: id}); return self._rpc({ route: '/web_editor/attachment/remove', params: { @@ -626,7 +588,7 @@ var FileWidget = SearchableMediaWidget.extend({ }, }).then(function (prevented) { if (_.isEmpty(prevented)) { - self.records = _.without(self.records, attachment); + self.attachments = _.without(self.attachments, attachment); self._renderImages(); return; } @@ -683,8 +645,7 @@ var FileWidget = SearchableMediaWidget.extend({ * @override */ _onSearchInput: function () { - this.imagesRows = this.IMAGES_ROWS; - this.IMAGES_DISPLAYED_TOTAL = this.IMAGES_PER_ROW * this.imagesRows; + this.numberOfAttachmentsToDisplay = this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY; this._super.apply(this, arguments); }, }); @@ -885,10 +846,10 @@ var IconWidget = SearchableMediaWidget.extend({ */ _highlightSelectedIcon: function () { var self = this; - this.$icons.removeClass('o_selected'); + this.$icons.removeClass('o_we_attachment_selected'); this.$icons.filter(function (i, el) { return _.contains($(el).data('alias').split(','), self.selectedIcon); - }).addClass('o_selected'); + }).addClass('o_we_attachment_selected'); }, //-------------------------------------------------------------------------- diff --git a/addons/web_editor/static/src/scss/wysiwyg.scss b/addons/web_editor/static/src/scss/wysiwyg.scss index 952944fe4ce..2cdb6ee1fca 100644 --- a/addons/web_editor/static/src/scss/wysiwyg.scss +++ b/addons/web_editor/static/src/scss/wysiwyg.scss @@ -287,51 +287,52 @@ body .modal { min-height: 300px; } - .existing-attachments { - width: 100%; + .o_we_images .o_existing_attachment_cell { + // give enough space for the hover buttons + min-width: 64px; + img { + // prevent url images from being too big + max-width: 256px; + max-height: 192px; + } } .o_existing_attachment_cell { - $o-border-image: 2px; + cursor: pointer; + list-style: none; + + // prevent hover/select border from flickering + border: 1px solid transparent; .o_existing_attachment_remove { - @include o-position-absolute($o-border-image, 15px + $o-border-image); - padding: 3px 5px 5px 5px; - background-color: rgba(white, 0.8); + @include o-position-absolute(0, 0); + background-color: scale-color($card-border-color, $alpha: -50%); text-shadow: 0 0 1px white; border-radius: 0 0 0 2px; opacity: 0; cursor: pointer; &:hover { - background-color: white; + background-color: $card-border-color; color: $o-wysiwyg-color-danger; } } - .o_image { - border: $o-border-image solid transparent; - cursor: pointer; - transition: opacity 400ms ease 0s; - - &.o_webimage { - width: 100%; - height: auto; - background-image: none !important; + &:hover { + .o_existing_attachment_remove { + opacity: 1; } - - &.o_image_loading { - opacity: 0; + &.o_we_attachment_highlight, .o_we_attachment_highlight { + border-color: $card-border-color; + box-shadow: 0px 0px 2px 2px $card-border-color; } } - &.o_selected .o_image { - border-color: $o-brand-odoo; - box-shadow: 0px 0px 2px 2px $o-brand-secondary; - } - - &:hover .o_existing_attachment_remove { - opacity: 1; + &.o_we_attachment_selected { + &.o_we_attachment_highlight, .o_we_attachment_highlight { + border-color: $o-brand-primary; + box-shadow: 0px 0px 2px 2px $o-brand-primary; + } } } diff --git a/addons/web_editor/static/src/xml/wysiwyg.xml b/addons/web_editor/static/src/xml/wysiwyg.xml index a360ec2daef..9f3101cb1c6 100644 --- a/addons/web_editor/static/src/xml/wysiwyg.xml +++ b/addons/web_editor/static/src/xml/wysiwyg.xml @@ -194,22 +194,47 @@ </div> </div> </t> + <t t-name="wysiwyg.widgets.image.existing.attachments"> - <div class="existing-attachments"> - <div class="row mt16 ml-auto" t-as="row" t-foreach="rows"> - <div class="col-2 o_existing_attachment_cell" t-as="attachment" t-foreach="row"> - <i t-if="attachment.res_model === 'ir.ui.view'" class="fa fa-trash o_existing_attachment_remove" title="This file is a public view attachment" role="img" aria-label="This file is a public view attachment" t-att-data-id="attachment.id"/> - <i t-else="" class="fa fa-trash o_existing_attachment_remove" title="This file is attached to the current record" role="img" aria-label="This file is attached to the current record" t-att-data-id="attachment.id"/> - <div class="o_attachment_border" t-att-style="attachment.res_model === 'ir.ui.view' ? null : 'border: 1px solid #5cb85c;'"><div t-att-data-src="attachment.src" t-att-data-url="attachment.url" role="img" t-att-aria-label="attachment.name" t-att-title="attachment.name" t-att-data-id="attachment.id" t-att-data-mimetype="attachment.mimetype" t-attf-class="o_image {{withEffect ? 'o_image_loading' : ''}}"/></div> - </div> + <div class="o_we_images"> + <ol class="existing-attachments d-flex flex-wrap mx-auto my-0 p-2"> + <t t-foreach="attachments" t-as="attachment"> + <t t-call="wysiwyg.widgets.image.existing.attachment"/> + </t> + </ol> + </div> + </t> + + <t t-name="wysiwyg.widgets.image.existing.attachment"> + <li class="o_existing_attachment_cell position-relative m-2" t-att-data-id="attachment.id"> + <t t-call="wysiwyg.widgets.file.existing.remove"/> + <div t-attf-class="d-flex align-items-center h-100"> + <img class="img img-fluid o_we_attachment_highlight" t-att-src="attachment.src + '?width=256&height=192'" t-att-alt="attachment.name" t-att-title="attachment.name"/> + </div> + </li> + </t> + + <t t-name="wysiwyg.widgets.document.existing.attachments"> + <div class="o_we_documents"> + <div class="row mx-auto existing-attachments"> + <t t-foreach="attachments" t-as="attachment"> + <div class="col-2 o_existing_attachment_cell o_we_attachment_highlight my-2" t-att-data-id="attachment.id"> + <t t-call="wysiwyg.widgets.file.existing.remove"/> + + <div t-att-data-url="attachment.url" role="img" t-att-aria-label="attachment.name" t-att-title="attachment.name" t-att-data-mimetype="attachment.mimetype" t-attf-class="o_image d-flex align-items-center justify-content-center"/> + <small class="o_file_name" t-esc="attachment.name"/> + </div> + </t> </div> </div> </t> - <t t-name="wysiwyg.widgets.document.existing.attachments" t-extend="wysiwyg.widgets.image.existing.attachments"> - <t t-jquery=".o_existing_attachment_cell" t-operation="append"> - <small class="o_file_name"><t t-esc="attachment.name"/></small> - </t> + + <t t-name="wysiwyg.widgets.file.existing.remove"> + <t t-set="removeTitle" t-if="attachment.res_model === 'ir.ui.view'">This file is a public view attachment.</t> + <t t-set="removeTitle" t-else="">This file is attached to the current record.</t> + <i class="fa fa-trash o_existing_attachment_remove p-2" t-att-title="removeTitle" role="img" t-att-aria-label="removeTitle"/> </t> + <t t-name="wysiwyg.widgets.image.existing.error"> <div class="form-text"> <p>The image could not be deleted because it is used in the diff --git a/addons/web_editor/static/tests/field_html_tests.js b/addons/web_editor/static/tests/field_html_tests.js index 477519d4060..d307706557f 100644 --- a/addons/web_editor/static/tests/field_html_tests.js +++ b/addons/web_editor/static/tests/field_html_tests.js @@ -194,6 +194,9 @@ QUnit.module('web_editor', {}, function () { return Promise.resolve(); } } + if (route.indexOf('/web_editor/static/src/img/') === 0) { + return Promise.resolve(); + } return this._super(route, args); }, }); @@ -216,7 +219,7 @@ QUnit.module('web_editor', {}, function () { // load static xml file (dialog, media dialog, unsplash image widget) await defMediaDialog; - await testUtils.dom.click($('.modal #editor-media-image .o_image:first')); + await testUtils.dom.click($('.modal #editor-media-image .o_existing_attachment_cell:first')); await testUtils.dom.click($('.modal .modal-footer button.btn-primary')); var $editable = form.$('.oe_form_field[name="body"] .note-editable'); diff --git a/addons/web_editor/static/tests/wysiwyg_snippets_tests.js b/addons/web_editor/static/tests/wysiwyg_snippets_tests.js index bd63364d303..842aeea1f2f 100644 --- a/addons/web_editor/static/tests/wysiwyg_snippets_tests.js +++ b/addons/web_editor/static/tests/wysiwyg_snippets_tests.js @@ -330,6 +330,12 @@ QUnit.test('background-image', function (assert) { ' </div>' + '</section>', }, + mockRPC: function (route, args) { + if (route.indexOf('/web_editor/static/src/img/') === 0) { + return Promise.resolve(); + } + return this._super(route, args); + }, }).then(function (wysiwyg) { var $editable = wysiwyg.getEditable(); @@ -352,7 +358,7 @@ QUnit.test('background-image', function (assert) { $('#oe_manipulators .oe_overlay_options a.snippet-option-background[data-choose-image]').click(); defMediaDialogInit.then(function () { - $('.o_select_media_dialog .existing-attachments .o_image').click(); + $('.o_select_media_dialog .existing-attachments .o_existing_attachment_cell').click(); $('.o_select_media_dialog .modal-footer .btn-primary').click(); }); diff --git a/addons/web_editor/static/tests/wysiwyg_tests.js b/addons/web_editor/static/tests/wysiwyg_tests.js index 4312121015b..be62dbb4192 100644 --- a/addons/web_editor/static/tests/wysiwyg_tests.js +++ b/addons/web_editor/static/tests/wysiwyg_tests.js @@ -3412,6 +3412,9 @@ QUnit.module('Media', { if (route.indexOf('youtube') !== -1) { return Promise.resolve(); } + if (route.indexOf('/web_editor/static/src/img/') === 0) { + return Promise.resolve(); + } return this._super(route, args); }, }; |
