summaryrefslogtreecommitdiff
path: root/addons/mass_mailing/static/src/js/mass_mailing_widget.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/mass_mailing/static/src/js/mass_mailing_widget.js')
-rw-r--r--addons/mass_mailing/static/src/js/mass_mailing_widget.js520
1 files changed, 520 insertions, 0 deletions
diff --git a/addons/mass_mailing/static/src/js/mass_mailing_widget.js b/addons/mass_mailing/static/src/js/mass_mailing_widget.js
new file mode 100644
index 00000000..b39cb917
--- /dev/null
+++ b/addons/mass_mailing/static/src/js/mass_mailing_widget.js
@@ -0,0 +1,520 @@
+odoo.define('mass_mailing.FieldHtml', function (require) {
+'use strict';
+
+var config = require('web.config');
+var core = require('web.core');
+var FieldHtml = require('web_editor.field.html');
+var fieldRegistry = require('web.field_registry');
+var convertInline = require('web_editor.convertInline');
+
+var _t = core._t;
+
+
+var MassMailingFieldHtml = FieldHtml.extend({
+ xmlDependencies: (FieldHtml.prototype.xmlDependencies || []).concat(["/mass_mailing/static/src/xml/mass_mailing.xml"]),
+ jsLibs: [
+ '/mass_mailing/static/src/js/mass_mailing_link_dialog_fix.js',
+ ],
+
+ custom_events: _.extend({}, FieldHtml.prototype.custom_events, {
+ snippets_loaded: '_onSnippetsLoaded',
+ }),
+
+ /**
+ * @override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ if (!this.nodeOptions.snippets) {
+ this.nodeOptions.snippets = 'mass_mailing.email_designer_snippets';
+ }
+
+ // All the code related to this __extraAssetsForIframe variable is an
+ // ugly hack to restore mass mailing options in stable versions. The
+ // whole logic has to be refactored as soon as possible...
+ this.__extraAssetsForIframe = [{
+ jsLibs: ['/mass_mailing/static/src/js/mass_mailing_snippets.js'],
+ }];
+ },
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * Commit the change in 'style-inline' on an other field nodeOptions:
+ *
+ * - inline-field: fieldName to save the html value converted into inline code
+ *
+ * @override
+ */
+ commitChanges: function () {
+ var self = this;
+ if (config.isDebug() && this.mode === 'edit') {
+ var layoutInfo = $.summernote.core.dom.makeLayoutInfo(this.wysiwyg.$editor);
+ $.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, false);
+ }
+ if (this.mode === 'readonly' || !this.isRendered) {
+ return this._super();
+ }
+ var fieldName = this.nodeOptions['inline-field'];
+
+ if (this.$content.find('.o_basic_theme').length) {
+ this.$content.find('*').css('font-family', '');
+ }
+
+ var $editable = this.wysiwyg.getEditable();
+
+ return this.wysiwyg.saveModifiedImages(this.$content).then(function () {
+ return self.wysiwyg.save().then(function (result) {
+ self._isDirty = result.isDirty;
+
+ convertInline.attachmentThumbnailToLinkImg($editable);
+ convertInline.fontToImg($editable);
+ convertInline.classToStyle($editable);
+
+ // fix outlook image rendering bug
+ _.each(['width', 'height'], function(attribute) {
+ $editable.find('img[style*="width"], img[style*="height"]').attr(attribute, function(){
+ return $(this)[attribute]();
+ }).css(attribute, function(){
+ return $(this).get(0).style[attribute] || 'auto';
+ });
+ });
+
+ self.trigger_up('field_changed', {
+ dataPointID: self.dataPointID,
+ changes: _.object([fieldName], [self._unWrap($editable.html())])
+ });
+ self.wysiwyg.setValue(result.html);
+
+ if (self._isDirty && self.mode === 'edit') {
+ return self._doAction();
+ }
+ });
+ });
+ },
+ /**
+ * The html_frame widget is opened in an iFrame that has its URL encoded
+ * with all the key/values returned by this method.
+ *
+ * Some fields can get very long values and we want to omit them for the URL building.
+ *
+ * @override
+ */
+ getDatarecord: function () {
+ return _.omit(this._super(), [
+ 'mailing_domain',
+ 'contact_list_ids',
+ 'body_html',
+ 'attachment_ids'
+ ]);
+ },
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Returns true if must force the user to choose a theme.
+ *
+ * @private
+ * @returns {Boolean}
+ */
+ _checkIfMustForceThemeChoice: function () {
+ var firstChoice = this._editableAreaIsEmpty();
+ this.$content.closest('body').toggleClass("o_force_mail_theme_choice", firstChoice);
+ return firstChoice;
+ },
+ /**
+ * Returns true if the editable area is empty.
+ *
+ * @private
+ * @param {JQuery} [$layout]
+ * @returns {Boolean}
+ */
+ _editableAreaIsEmpty: function ($layout) {
+ $layout = $layout || this.$content.find(".o_layout");
+ var $mailWrapper = $layout.children(".o_mail_wrapper");
+ var $mailWrapperContent = $mailWrapper.find('.o_mail_wrapper_td');
+ if (!$mailWrapperContent.length) { // compatibility
+ $mailWrapperContent = $mailWrapper;
+ }
+ var value;
+ if ($mailWrapperContent.length > 0) {
+ value = $mailWrapperContent.html();
+ } else if ($layout.length) {
+ value = $layout.html();
+ } else {
+ value = this.wysiwyg.getValue();
+ }
+ var blankEditable = "<p><br></p>";
+ return value === "" || value === blankEditable;
+ },
+ /**
+ * @override
+ */
+ _renderEdit: function () {
+ this._isFromInline = !!this.value;
+ if (!this.value) {
+ this.value = this.recordData[this.nodeOptions['inline-field']];
+ }
+ return this._super.apply(this, arguments);
+ },
+ /**
+ * @override
+ */
+ _renderReadonly: function () {
+ this.value = this.recordData[this.nodeOptions['inline-field']];
+ return this._super.apply(this, arguments);
+ },
+
+ /**
+ * @override
+ * @returns {JQuery}
+ */
+ _renderTranslateButton: function () {
+ var fieldName = this.nodeOptions['inline-field'];
+ if (_t.database.multi_lang && this.record.fields[fieldName].translate && this.res_id) {
+ return $('<button>', {
+ type: 'button',
+ 'class': 'o_field_translate fa fa-globe btn btn-link',
+ })
+ .on('click', this._onTranslate.bind(this));
+ }
+ return $();
+ },
+ /**
+ * Returns the selected theme, if any.
+ *
+ * @private
+ * @param {Object} themesParams
+ * @returns {false|Object}
+ */
+ _getSelectedTheme: function (themesParams) {
+ var $layout = this.$content.find(".o_layout");
+ var selectedTheme = false;
+ if ($layout.length !== 0) {
+ _.each(themesParams, function (themeParams) {
+ if ($layout.hasClass(themeParams.className)) {
+ selectedTheme = themeParams;
+ }
+ });
+ }
+ return selectedTheme;
+ },
+ /**
+ * Swap the previous theme's default images with the new ones.
+ * (Redefine the `src` attribute of all images in a $container, depending on the theme parameters.)
+ *
+ * @private
+ * @param {Object} themeParams
+ * @param {JQuery} $container
+ */
+ _switchImages: function (themeParams, $container) {
+ if (!themeParams) {
+ return;
+ }
+ $container.find("img").each(function () {
+ var $img = $(this);
+ var src = $img.attr("src");
+
+ var m = src.match(/^\/web\/image\/\w+\.s_default_image_(?:theme_[a-z]+_)?(.+)$/);
+ if (!m) {
+ m = src.match(/^\/\w+\/static\/src\/img\/(?:theme_[a-z]+\/)?s_default_image_(.+)\.[a-z]+$/);
+ }
+ if (!m) {
+ return;
+ }
+
+ var file = m[1];
+ var img_info = themeParams.get_image_info(file);
+
+ if (img_info.format) {
+ src = "/" + img_info.module + "/static/src/img/theme_" + themeParams.name + "/s_default_image_" + file + "." + img_info.format;
+ } else {
+ src = "/web/image/" + img_info.module + ".s_default_image_theme_" + themeParams.name + "_" + file;
+ }
+
+ $img.attr("src", src);
+ });
+ },
+ /**
+ * Switch themes or import first theme.
+ *
+ * @private
+ * @param {Boolean} firstChoice true if this is the first chosen theme (going from no theme to a theme)
+ * @param {Object} themeParams
+ */
+ _switchThemes: function (firstChoice, themeParams) {
+ if (!themeParams || this.switchThemeLast === themeParams) {
+ return;
+ }
+ this.switchThemeLast = themeParams;
+
+ this.$content.closest('body').removeClass(this._allClasses).addClass(themeParams.className);
+
+ var $old_layout = this.$content.find('.o_layout');
+
+ var $new_wrapper;
+ var $newWrapperContent;
+ if (themeParams.nowrap) {
+ $new_wrapper = $('<div/>', {
+ class: 'oe_structure'
+ });
+ $newWrapperContent = $new_wrapper;
+ } else {
+ // This wrapper structure is the only way to have a responsive
+ // and centered fixed-width content column on all mail clients
+ $new_wrapper = $('<table/>', {
+ class: 'o_mail_wrapper'
+ });
+ $newWrapperContent = $('<td/>', {
+ class: 'o_mail_no_options o_mail_wrapper_td oe_structure'
+ });
+ $new_wrapper.append($('<tr/>').append(
+ $('<td/>', {
+ class: 'o_mail_no_resize o_not_editable',
+ contenteditable: 'false'
+ }),
+ $newWrapperContent,
+ $('<td/>', {
+ class: 'o_mail_no_resize o_not_editable',
+ contenteditable: 'false'
+ })
+ ));
+ }
+ var $newLayout = $('<div/>', {
+ class: 'o_layout ' + themeParams.className
+ }).append($new_wrapper);
+
+ var $contents;
+ if (firstChoice) {
+ $contents = themeParams.template;
+ } else if ($old_layout.length) {
+ $contents = ($old_layout.hasClass('oe_structure') ? $old_layout : $old_layout.find('.oe_structure').first()).contents();
+ } else {
+ $contents = this.$content.find('.o_editable').contents();
+ }
+
+ $newWrapperContent.append($contents);
+ this._switchImages(themeParams, $newWrapperContent);
+ this.$content.find('.o_editable').empty().append($newLayout);
+ $old_layout.remove();
+
+ if (firstChoice) {
+ $newWrapperContent.find('*').addBack()
+ .contents()
+ .filter(function () {
+ return this.nodeType === 3 && this.textContent.match(/\S/);
+ }).parent().addClass('o_default_snippet_text');
+
+ if (themeParams.name == 'basic') {
+ this.$content.focusIn();
+ }
+ }
+ this.wysiwyg.trigger('reload_snippet_dropzones');
+ },
+
+ //--------------------------------------------------------------------------
+ // Handler
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _onLoadWysiwyg: function () {
+ if (this._isFromInline) {
+ this._fromInline();
+ }
+ if (this.snippetsLoaded) {
+ this._onSnippetsLoaded(this.snippetsLoaded);
+ }
+ this._super();
+ },
+ /**
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onSnippetsLoaded: function (ev) {
+ var self = this;
+ if (!this.$content) {
+ this.snippetsLoaded = ev;
+ return;
+ }
+ var $snippetsSideBar = ev.data;
+ var $themes = $snippetsSideBar.find("#email_designer_themes").children();
+ var $snippets = $snippetsSideBar.find(".oe_snippet");
+ var $snippets_menu = $snippetsSideBar.find("#snippets_menu");
+
+ if (config.device.isMobile) {
+ $snippetsSideBar.hide();
+ this.$content.attr('style', 'padding-left: 0px !important');
+ }
+
+ if ($themes.length === 0) {
+ return;
+ }
+
+ /**
+ * Initialize theme parameters.
+ */
+ this._allClasses = "";
+ var themesParams = _.map($themes, function (theme) {
+ var $theme = $(theme);
+ var name = $theme.data("name");
+ var classname = "o_" + name + "_theme";
+ self._allClasses += " " + classname;
+ var imagesInfo = _.defaults($theme.data("imagesInfo") || {}, {
+ all: {}
+ });
+ _.each(imagesInfo, function (info) {
+ info = _.defaults(info, imagesInfo.all, {
+ module: "mass_mailing",
+ format: "jpg"
+ });
+ });
+ return {
+ name: name,
+ className: classname || "",
+ img: $theme.data("img") || "",
+ template: $theme.html().trim(),
+ nowrap: !!$theme.data('nowrap'),
+ get_image_info: function (filename) {
+ if (imagesInfo[filename]) {
+ return imagesInfo[filename];
+ }
+ return imagesInfo.all;
+ }
+ };
+ });
+ $themes.parent().remove();
+
+ /**
+ * Create theme selection screen and check if it must be forced opened.
+ * Reforce it opened if the last snippet is removed.
+ */
+ var $dropdown = $(core.qweb.render("mass_mailing.theme_selector", {
+ themes: themesParams
+ })).dropdown();
+
+ var firstChoice = this._checkIfMustForceThemeChoice();
+
+ /**
+ * Add proposition to install enterprise themes if not installed.
+ */
+ var $mail_themes_upgrade = $dropdown.find(".o_mass_mailing_themes_upgrade");
+ $mail_themes_upgrade.on("click", function (e) {
+ e.stopImmediatePropagation();
+ e.preventDefault();
+ self.do_action("mass_mailing.action_mass_mailing_configuration");
+ });
+
+ /**
+ * Switch theme when a theme button is hovered. Confirm change if the theme button
+ * is pressed.
+ */
+ var selectedTheme = false;
+ $dropdown.on("mouseenter", ".dropdown-item", function (e) {
+ if (firstChoice) {
+ return;
+ }
+ e.preventDefault();
+ var themeParams = themesParams[$(e.currentTarget).index()];
+ self._switchThemes(firstChoice, themeParams);
+ });
+ $dropdown.on("mouseleave", ".dropdown-item", function (e) {
+ self._switchThemes(false, selectedTheme);
+ });
+ $dropdown.on("click", '[data-toggle="dropdown"]', function (e) {
+ var $menu = $dropdown.find('.dropdown-menu');
+ var isVisible = $menu.hasClass('show');
+ if (isVisible) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ $menu.removeClass('show');
+ }
+ });
+
+ $dropdown.on("click", ".dropdown-item", function (e) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ var themeParams = themesParams[$(e.currentTarget).index()];
+ if (firstChoice) {
+ self._switchThemes(firstChoice, themeParams);
+ self.$content.closest('body').removeClass("o_force_mail_theme_choice");
+ firstChoice = false;
+
+ if ($mail_themes_upgrade.length) {
+ $dropdown.remove();
+ $snippets_menu.empty();
+ }
+ }
+
+ self._switchImages(themeParams, $snippets);
+
+ selectedTheme = themeParams;
+
+ // Notify form view
+ self.wysiwyg.getEditable().trigger('change');
+ $dropdown.find('.dropdown-menu').removeClass('show');
+ $dropdown.find('.dropdown-item.selected').removeClass('selected');
+ $dropdown.find('.dropdown-item:eq(' + themesParams.indexOf(selectedTheme) + ')').addClass('selected');
+ });
+
+ /**
+ * If the user opens the theme selection screen, indicates which one is active and
+ * saves the information...
+ * ... then when the user closes check if the user confirmed its choice and restore
+ * previous state if this is not the case.
+ */
+ $dropdown.on("shown.bs.dropdown", function () {
+ selectedTheme = self._getSelectedTheme(themesParams);
+ $dropdown.find(".dropdown-item").removeClass("selected").filter(function () {
+ return ($(this).has(".o_thumb[style=\"" + "background-image: url(" + (selectedTheme && selectedTheme.img) + "_small.png)" + "\"]").length > 0);
+ }).addClass("selected");
+ });
+ $dropdown.on("hidden.bs.dropdown", function () {
+ self._switchThemes(firstChoice, selectedTheme);
+ });
+
+ /**
+ * On page load, check the selected theme and force switching to it (body needs the
+ * theme style for its edition toolbar).
+ */
+ selectedTheme = this._getSelectedTheme(themesParams);
+ if (selectedTheme) {
+ this.$content.closest('body').addClass(selectedTheme.className);
+ $dropdown.find('.dropdown-item:eq(' + themesParams.indexOf(selectedTheme) + ')').addClass('selected');
+ this._switchImages(selectedTheme, $snippets);
+ } else if (this.$content.find('.o_layout').length) {
+ themesParams.push({
+ name: 'o_mass_mailing_no_theme',
+ className: 'o_mass_mailing_no_theme',
+ img: "",
+ template: this.$content.find('.o_layout').addClass('o_mass_mailing_no_theme').clone().find('oe_structure').empty().end().html().trim(),
+ nowrap: true,
+ get_image_info: function () {}
+ });
+ selectedTheme = this._getSelectedTheme(themesParams);
+ }
+
+ $dropdown.insertAfter($snippets_menu);
+ },
+ /**
+ * @override
+ * @param {MouseEvent} ev
+ */
+ _onTranslate: function (ev) {
+ this.trigger_up('translate', {
+ fieldName: this.nodeOptions['inline-field'],
+ id: this.dataPointID,
+ isComingFromTranslationAlert: false,
+ });
+ },
+});
+
+fieldRegistry.add('mass_mailing_html', MassMailingFieldHtml);
+
+return MassMailingFieldHtml;
+
+});