[IMP] web_editor, web_unsplash: split media widget (image & document)
The common code is kept in a generic File widget, while the split allows for easier customization. PR: #33509
This commit is contained in:
@@ -97,10 +97,9 @@ var SearchableMediaWidget = MediaWidget.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 = SearchableMediaWidget.extend({
|
||||
template: 'wysiwyg.widgets.image',
|
||||
var FileWidget = SearchableMediaWidget.extend({
|
||||
events: _.extend({}, SearchableMediaWidget.prototype.events || {}, {
|
||||
'click .o_upload_media_button': '_onUploadButtonClick',
|
||||
'click .o_upload_media_button_no_optimization': '_onUploadButtonNoOptimizationClick',
|
||||
@@ -112,7 +111,9 @@ var ImageWidget = SearchableMediaWidget.extend({
|
||||
'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,
|
||||
|
||||
@@ -128,7 +129,7 @@ var ImageWidget = SearchableMediaWidget.extend({
|
||||
|
||||
this.options = options;
|
||||
this.context = options.context;
|
||||
this.accept = options.accept || (options.document ? '*/*' : 'image/*');
|
||||
this.accept = options.accept;
|
||||
|
||||
this.multiImages = options.multiImages;
|
||||
|
||||
@@ -299,10 +300,7 @@ var ImageWidget = SearchableMediaWidget.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]);
|
||||
}
|
||||
@@ -337,6 +335,20 @@ var ImageWidget = SearchableMediaWidget.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
|
||||
*/
|
||||
@@ -352,11 +364,7 @@ var ImageWidget = SearchableMediaWidget.extend({
|
||||
|
||||
// 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');
|
||||
@@ -481,6 +489,26 @@ var ImageWidget = SearchableMediaWidget.extend({
|
||||
this.search('');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Updates the add by URL UI.
|
||||
*
|
||||
* @private
|
||||
* @param {boolean} emptyValue
|
||||
* @param {boolean} isURL
|
||||
* @param {boolean} isImage
|
||||
*/
|
||||
_updateAddUrlUi(emptyValue, isURL, isImage) {
|
||||
var $button = this.$('.o_upload_media_url_button');
|
||||
var $success = this.$('.o_we_url_success');
|
||||
var $error = this.$('.o_we_url_error');
|
||||
|
||||
$button.toggleClass('btn-secondary', emptyValue)
|
||||
.toggleClass('btn-primary', !emptyValue)
|
||||
.prop('disabled', !isURL);
|
||||
|
||||
$success.toggleClass('d-none', !isURL);
|
||||
$error.toggleClass('d-none', emptyValue || isURL);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -604,6 +632,7 @@ var ImageWidget = SearchableMediaWidget.extend({
|
||||
}
|
||||
$helpBlock.replaceWith(QWeb.render('wysiwyg.widgets.image.existing.error', {
|
||||
views: prevented[id],
|
||||
widget: self,
|
||||
}));
|
||||
});
|
||||
}
|
||||
@@ -614,10 +643,6 @@ var ImageWidget = SearchableMediaWidget.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 === '');
|
||||
@@ -627,14 +652,7 @@ var ImageWidget = SearchableMediaWidget.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
|
||||
@@ -671,6 +689,79 @@ var ImageWidget = SearchableMediaWidget.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);
|
||||
|
||||
var $button = this.$('.o_upload_media_url_button');
|
||||
var $warning = this.$('.o_we_url_warning');
|
||||
|
||||
$button.text((isURL && !isImage) ? _t("Add as document") : _t("Add image"));
|
||||
$warning.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);
|
||||
|
||||
var $button = this.$('.o_upload_media_url_button');
|
||||
var $warning = this.$('.o_we_url_warning');
|
||||
|
||||
$button.text((isURL && isImage) ? _t("Add as image") : _t("Add document"));
|
||||
$warning.toggleClass('d-none', !isURL || !isImage);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Let users choose a font awesome icon, support all font awesome loaded in the
|
||||
* css files.
|
||||
@@ -758,7 +849,7 @@ var IconWidget = SearchableMediaWidget.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();
|
||||
},
|
||||
@@ -1118,7 +1209,9 @@ var VideoWidget = MediaWidget.extend({
|
||||
return {
|
||||
MediaWidget: MediaWidget,
|
||||
SearchableMediaWidget: SearchableMediaWidget,
|
||||
FileWidget: FileWidget,
|
||||
ImageWidget: ImageWidget,
|
||||
DocumentWidget: DocumentWidget,
|
||||
IconWidget: IconWidget,
|
||||
VideoWidget: VideoWidget,
|
||||
};
|
||||
|
||||
@@ -55,9 +55,7 @@ var MediaDialog = Dialog.extend({
|
||||
this.noVideos = onlyImages || options.noVideos;
|
||||
|
||||
if (!this.noDocuments) {
|
||||
this.documentDialog = new MediaModules.ImageWidget(this, this.media, _.extend({}, options, {
|
||||
document: true,
|
||||
}));
|
||||
this.documentDialog = new MediaModules.DocumentWidget(this, this.media, options);
|
||||
this.documentDialog.tabToShow = 'document';
|
||||
}
|
||||
if (!this.noIcons) {
|
||||
|
||||
@@ -20,7 +20,9 @@ return {
|
||||
|
||||
MediaWidget: media.MediaWidget,
|
||||
SearchableMediaWidget: media.SearchableMediaWidget,
|
||||
FileWidget: media.FileWidget,
|
||||
ImageWidget: media.ImageWidget,
|
||||
DocumentWidget: media.DocumentWidget,
|
||||
IconWidget: media.IconWidget,
|
||||
VideoWidget: media.VideoWidget,
|
||||
};
|
||||
|
||||
@@ -93,8 +93,8 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Image choosing part of the Media Dialog -->
|
||||
<t t-name="wysiwyg.widgets.image">
|
||||
<!-- File choosing part of the Media Dialog -->
|
||||
<t t-name="wysiwyg.widgets.file">
|
||||
<t t-set="iframeName" t-value="_.uniqueId('fileframe-')"/>
|
||||
<form method="POST"
|
||||
action="/web_editor/attachment/add"
|
||||
@@ -104,10 +104,7 @@
|
||||
<input type="hidden" name="filters" t-att-value="widget.firstFilters.join('_')"/>
|
||||
<input t-if="widget.options.res_id" type="hidden" name="res_id" t-att-value="widget.options.res_id"/>
|
||||
<input t-if="widget.options.res_model" type="hidden" name="res_model" t-att-value="widget.options.res_model"/>
|
||||
<t t-call="wysiwyg.widgets.files.submenu">
|
||||
<t t-set="isDocument" t-value="widget.options.document"/>
|
||||
<t t-set="accept" t-value="widget.accept"/>
|
||||
</t>
|
||||
<t t-call="wysiwyg.widgets.files.submenu"/>
|
||||
<div class="form-text"/>
|
||||
<div class="existing-attachments"/>
|
||||
<div class="mt-4 text-center mx-auto">
|
||||
@@ -122,48 +119,62 @@
|
||||
|
||||
<t t-name="wysiwyg.widgets.files.submenu">
|
||||
<div class="form-inline align-items-center py-4">
|
||||
<input type="file" class="d-none o_file_input" name="upload" t-att-accept="accept" multiple="multiple"/>
|
||||
<input type="file" class="d-none o_file_input" name="upload" t-att-accept="widget.accept" multiple="multiple"/>
|
||||
<input type="hidden" name="disable_optimization" value=""/>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary o_upload_media_button">
|
||||
<t t-if="isDocument">Upload a document</t>
|
||||
<t t-else="">Upload an image</t>
|
||||
<t t-esc="uploadText"/>
|
||||
</button>
|
||||
<t t-if="!isDocument">
|
||||
<button class="btn btn-primary dropdown-toggle dropdown-toggle-split o_media_optimization" data-toggle="dropdown" type="button">
|
||||
<span class="sr-only">Alternate Upload</span>
|
||||
</button>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<button type="button" role="menuitem" class="dropdown-item o_upload_media_button_no_optimization">Upload image without optimization</button>
|
||||
</div>
|
||||
</t>
|
||||
<t t-raw="alternateUpload"/>
|
||||
</div>
|
||||
|
||||
<div class="input-group align-items-center ml-2">
|
||||
<input type="text" class="form-control o_we_url_input" name="url" t-attf-placeholder="https://www.odoo.com/#{isDocument ? 'mydocument' : 'logo.png'}"/>
|
||||
<input type="text" class="form-control o_we_url_input" name="url" t-att-placeholder="urlPlaceholder"/>
|
||||
<div class="input-group-append align-items-center">
|
||||
<button type="button" class="btn btn-secondary o_upload_media_url_button" disabled="disabled">
|
||||
<t t-if="isDocument">Add document</t>
|
||||
<t t-else="">Add image</t>
|
||||
<t t-esc="addText"/>
|
||||
</button>
|
||||
<div class="ml-2">
|
||||
<span class="o_we_url_success text-success d-none fa fa-lg fa-check" title="The URL seems valid"/>
|
||||
<span class="o_we_url_warning text-warning d-none fa fa-lg fa-warning" title="The URL does not contain any image. The file will be added in the document section."/>
|
||||
<span class="o_we_url_error text-danger d-none fa fa-lg fa-times" title="The URL does not seem to work"/>
|
||||
<span class="o_we_url_success text-success d-none fa fa-lg fa-check" title="The URL seems valid."/>
|
||||
<span class="o_we_url_warning text-warning d-none fa fa-lg fa-warning" t-att-title="urlWarningTitle"/>
|
||||
<span class="o_we_url_error text-danger d-none fa fa-lg fa-times" title="The URL does not seem to work."/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<t t-call="wysiwyg.widgets.media.search">
|
||||
<t t-set="searchPlaceholder">
|
||||
<t t-if="isDocument">Search a document</t>
|
||||
<t t-else="">Search an image</t>
|
||||
</t>
|
||||
</t>
|
||||
<t t-call="wysiwyg.widgets.media.search"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="wysiwyg.widgets.image">
|
||||
<t t-call="wysiwyg.widgets.file">
|
||||
<t t-set="uploadText">Upload an image</t>
|
||||
<t t-set="urlPlaceholder">https://www.odoo.com/logo.png</t>
|
||||
<t t-set="addText">Add image</t>
|
||||
<t t-set="searchPlaceholder">Search an image</t>
|
||||
<t t-set="urlWarningTitle">The URL does not contain any image. The file will be added in the document section.</t>
|
||||
<t t-set="alternateUpload">
|
||||
<button class="btn btn-primary dropdown-toggle dropdown-toggle-split o_media_optimization" data-toggle="dropdown" type="button">
|
||||
<span class="sr-only">Alternate Upload</span>
|
||||
</button>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<button type="button" role="menuitem" class="dropdown-item o_upload_media_button_no_optimization">Upload image without optimization</button>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="wysiwyg.widgets.document">
|
||||
<t t-call="wysiwyg.widgets.file">
|
||||
<t t-set="uploadText">Upload a document</t>
|
||||
<t t-set="urlPlaceholder">https://www.odoo.com/mydocument</t>
|
||||
<t t-set="addText">Add document</t>
|
||||
<t t-set="searchPlaceholder">Search a document</t>
|
||||
<t t-set="urlWarningTitle">The URL contains an image. The file will be added in the image section.</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="wysiwyg.widgets.image.existing">
|
||||
<div role="dialog" class="modal" tabindex="-1">
|
||||
<div class="modal-dialog select-image">
|
||||
@@ -183,20 +194,22 @@
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="wysiwyg.widgets.files.existing.content">
|
||||
<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>
|
||||
<t t-if="isDocument">
|
||||
<small class="o_file_name"><t t-esc="attachment.name"/></small>
|
||||
</t>
|
||||
</div>
|
||||
</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 t-name="wysiwyg.widgets.image.existing.error">
|
||||
<div class="form-text">
|
||||
<p>The image could not be deleted because it is used in the
|
||||
|
||||
@@ -193,21 +193,6 @@ QUnit.module('web_editor', {}, function () {
|
||||
if (args.method === "generate_access_token") {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (args.kwargs.domain[7][2].join(',') === "image/gif,image/jpe,image/jpeg,image/jpg,image/gif,image/png") {
|
||||
return Promise.resolve([{
|
||||
"id": 1,
|
||||
"public": true,
|
||||
"name": "image",
|
||||
"datas_fname": "image.png",
|
||||
"mimetype": "image/png",
|
||||
"checksum": false,
|
||||
"url": "/web_editor/static/src/img/transparent.png",
|
||||
"type": "url",
|
||||
"res_id": 0,
|
||||
"res_model": false,
|
||||
"access_token": false
|
||||
}]);
|
||||
}
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
|
||||
@@ -3406,12 +3406,8 @@ QUnit.module('Media', {
|
||||
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (args.model === 'ir.attachment' || !args.length) {
|
||||
if (!args.length && route.indexOf('data:image/png;base64') === 0 ||
|
||||
args.method === "search_read" &&
|
||||
args.kwargs.domain[7][2].join(',') === "image/gif,image/jpe,image/jpeg,image/jpg,image/gif,image/png") {
|
||||
return Promise.resolve(this.data.records);
|
||||
}
|
||||
if (route.indexOf('data:image/png;base64') === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (route.indexOf('youtube') !== -1) {
|
||||
return Promise.resolve();
|
||||
|
||||
@@ -56,9 +56,7 @@ widgetsMedia.ImageWidget.include({
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
if (!this.options.document) {
|
||||
this.$('.o_we_search_icon').replaceWith(core.qweb.render('web_unsplash.dialog.media.search_icon'));
|
||||
}
|
||||
this.$('.o_we_search_icon').replaceWith(core.qweb.render('web_unsplash.dialog.media.search_icon'));
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
@@ -286,18 +284,16 @@ widgetsMedia.ImageWidget.include({
|
||||
* @override
|
||||
*/
|
||||
_onSearchInput: function (ev) {
|
||||
if (!this.options.document) {
|
||||
var $input = $(ev.currentTarget);
|
||||
var inputValue = $input.val();
|
||||
this.$('.o_search_value_input').text(inputValue);
|
||||
var $input = $(ev.currentTarget);
|
||||
var inputValue = $input.val();
|
||||
this.$('.o_search_value_input').text(inputValue);
|
||||
|
||||
var $icon = this.$('.o_we_search_icon');
|
||||
if ($icon.parent().is('.show')) {
|
||||
$icon.dropdown('update');
|
||||
} else if (!this.hasSearched) {
|
||||
$icon.dropdown('toggle');
|
||||
$input.focus();
|
||||
}
|
||||
var $icon = this.$('.o_we_search_icon');
|
||||
if ($icon.parent().is('.show')) {
|
||||
$icon.dropdown('update');
|
||||
} else if (!this.hasSearched) {
|
||||
$icon.dropdown('toggle');
|
||||
$input.focus();
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-extend="wysiwyg.widgets.image">
|
||||
<t t-extend="wysiwyg.widgets.file">
|
||||
<t t-jquery=".existing-attachments" t-operation="after">
|
||||
<div class="unsplash_img_container"></div>
|
||||
</t>
|
||||
|
||||
Reference in New Issue
Block a user