odoo.define('web_editor.backend', function (require) {
'use strict';
var AbstractField = require('web.AbstractField');
var basic_fields = require('web.basic_fields');
var config = require('web.config');
var core = require('web.core');
var session = require('web.session');
var field_registry = require('web.field_registry');
var SummernoteManager = require('web_editor.rte.summernote');
var transcoder = require('web_editor.transcoder');
var TranslatableFieldMixin = basic_fields.TranslatableFieldMixin;
var QWeb = core.qweb;
var _t = core._t;
/**
* FieldTextHtmlSimple Widget
* Intended to display HTML content. This widget uses the summernote editor
* improved by odoo.
*
*/
var FieldTextHtmlSimple = basic_fields.DebouncedField.extend(TranslatableFieldMixin, {
className: 'oe_form_field oe_form_field_html_text',
supportedFieldTypes: ['html'],
/**
* @override
*/
start: function () {
new SummernoteManager(this);
return this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Summernote doesn't notify for changes done in code mode. We override
* commitChanges to manually switch back to normal mode before committing
* changes, so that the widget is aware of the changes done in code mode.
*
* @override
*/
commitChanges: function () {
// switch to WYSIWYG mode if currently in code mode to get all changes
if (config.debug && this.mode === 'edit') {
var layoutInfo = this.$textarea.data('layoutInfo');
$.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, false);
}
if (this._getValue() !== this.value) {
this._isDirty = true;
}
this._super.apply(this, arguments);
},
/**
* @override
*/
isSet: function () {
return this.value && this.value !== "
" && this.value.match(/\S/);
},
/**
* Do not re-render this field if it was the origin of the onchange call.
*
* @override
*/
reset: function (record, event) {
this._reset(record, event);
if (!event || event.target !== this) {
if (this.mode === 'edit') {
this.$content.html(this._textToHtml(this.value));
} else {
this._renderReadonly();
}
}
return $.when();
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Returns the domain for attachments used in media dialog.
* We look for attachments related to the current document. If there is a value for the model
* field, it is used to search attachments, and the attachments from the current document are
* filtered to display only user-created documents.
* In the case of a wizard such as mail, we have the documents uploaded and those of the model
*
* @private
* @returns {Array} "ir.attachment" odoo domain.
*/
_getAttachmentsDomain: function () {
var domain = ['|', ['id', 'in', _.pluck(this.attachments, 'id')]];
var attachedDocumentDomain = [
'&',
['res_model', '=', this.model],
['res_id', '=', this.res_id|0]
];
// if the document is not yet created, do not see the documents of other users
if (!this.res_id) {
attachedDocumentDomain.unshift('&');
attachedDocumentDomain.push(['create_uid', '=', session.uid]);
}
if (this.recordData.res_model || this.recordData.model) {
var relatedDomain = ['&',
['res_model', '=', this.recordData.res_model || this.recordData.model],
['res_id', '=', this.recordData.res_id|0]];
if (!this.recordData.res_id) {
relatedDomain.unshift('&');
relatedDomain.push(['create_uid', '=', session.uid]);
}
domain = domain.concat(['|'], attachedDocumentDomain, relatedDomain);
} else {
domain = domain.concat(attachedDocumentDomain);
}
return domain;
},
/**
* @private
* @returns {Object} the summernote configuration
*/
_getSummernoteConfig: function () {
var summernoteConfig = {
model: this.model,
id: this.res_id,
focus: false,
height: 180,
toolbar: [
['style', ['style']],
['font', ['bold', 'italic', 'underline', 'clear']],
['fontsize', ['fontsize']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['table', ['table']],
['insert', this.nodeOptions['no-attachment'] ? ['link'] : ['link', 'picture']],
['history', ['undo', 'redo']]
],
prettifyHtml: false,
styleWithSpan: false,
inlinemedia: ['p'],
lang: "odoo",
onChange: this._doDebouncedAction.bind(this),
disableDragAndDrop: !!this.nodeOptions['no-attachment'],
};
var fieldNameAttachment =_.chain(this.recordData)
.pairs()
.find(function (value) {
return _.isObject(value[1]) && value[1].model === "ir.attachment";
})
.first()
.value();
if (fieldNameAttachment) {
this.fieldNameAttachment = fieldNameAttachment;
this.attachments = [];
summernoteConfig.onUpload = this._onUpload.bind(this);
}
summernoteConfig.getMediaDomain = this._getAttachmentsDomain.bind(this);
if (config.debug) {
summernoteConfig.toolbar.splice(7, 0, ['view', ['codeview']]);
}
return summernoteConfig;
},
/**
* @override
* @private
*/
_getValue: function () {
if (this.nodeOptions['style-inline']) {
transcoder.attachmentThumbnailToLinkImg(this.$content);
transcoder.fontToImg(this.$content);
transcoder.classToStyle(this.$content);
}
return this.$content.html();
},
/**
* trigger_up 'field_changed' add record into the "ir.attachment" field found in the view.
* This method is called when an image is uploaded by the media dialog.
*
* For e.g. when sending email, this allows people to add attachments with the content
* editor interface and that they appear in the attachment list.
* The new documents being attached to the email, they will not be erased by the CRON
* when closing the wizard.
*
* @private
*/
_onUpload: function (attachments) {
var self = this;
attachments = _.filter(attachments, function (attachment) {
return !_.findWhere(self.attachments, {id: attachment.id});
});
if (!attachments.length) {
return;
}
this.attachments = this.attachments.concat(attachments);
this.trigger_up('field_changed', {
dataPointID: this.dataPointID,
changes: _.object([this.fieldNameAttachment], [{
operation: 'ADD_M2M',
ids: attachments
}])
});
},
/**
* @override
* @private
*/
_renderEdit: function () {
this.$textarea = $('