diff options
Diffstat (limited to 'addons/mass_mailing/static/src')
70 files changed, 1776 insertions, 0 deletions
diff --git a/addons/mass_mailing/static/src/css/basic_theme_readonly.css b/addons/mass_mailing/static/src/css/basic_theme_readonly.css new file mode 100644 index 00000000..340eec95 --- /dev/null +++ b/addons/mass_mailing/static/src/css/basic_theme_readonly.css @@ -0,0 +1,7 @@ +/* We want to show system's default font in the mail sent with the basic theme. + And so we remove font-family from basic theme while saving. But by doing so + in readonly mode is not getting proper font in odoo. So we added if from here. + */ +#wrapwrap .o_layout.o_basic_theme { + font-family: -apple-system, "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; +} diff --git a/addons/mass_mailing/static/src/css/email_template.css b/addons/mass_mailing/static/src/css/email_template.css new file mode 100644 index 00000000..edd9c0c3 --- /dev/null +++ b/addons/mass_mailing/static/src/css/email_template.css @@ -0,0 +1,21 @@ +.openerp .oe_kanban_email_template { + width: 360px; + height: 340px; +} + +.openerp .oe_kanban_email_template .oe_kanban_content{ + height: 320px; + overflow: hidden !important; +} + +.kanban_html_preview { + pointer-events: none; + width: 600px; + -webkit-transform: scale(.50); + -ms-transform: scale(.50); + transform: scale(.50); + -webkit-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; + margin: 0; +} diff --git a/addons/mass_mailing/static/src/img/blocks/block_banner.png b/addons/mass_mailing/static/src/img/blocks/block_banner.png Binary files differnew file mode 100755 index 00000000..bcf62bd1 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_banner.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_comparison_table.png b/addons/mass_mailing/static/src/img/blocks/block_comparison_table.png Binary files differnew file mode 100755 index 00000000..63c810cc --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_comparison_table.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_discount1.png b/addons/mass_mailing/static/src/img/blocks/block_discount1.png Binary files differnew file mode 100755 index 00000000..478d2525 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_discount1.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_discount2.png b/addons/mass_mailing/static/src/img/blocks/block_discount2.png Binary files differnew file mode 100755 index 00000000..0a14a917 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_discount2.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_event.png b/addons/mass_mailing/static/src/img/blocks/block_event.png Binary files differnew file mode 100755 index 00000000..4dba58c9 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_event.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_footer_separator.png b/addons/mass_mailing/static/src/img/blocks/block_footer_separator.png Binary files differnew file mode 100755 index 00000000..6af266fc --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_footer_separator.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_footer_social.png b/addons/mass_mailing/static/src/img/blocks/block_footer_social.png Binary files differnew file mode 100755 index 00000000..41992607 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_footer_social.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_footer_social_left.png b/addons/mass_mailing/static/src/img/blocks/block_footer_social_left.png Binary files differnew file mode 100755 index 00000000..ee39ed7c --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_footer_social_left.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_footer_tag_line.png b/addons/mass_mailing/static/src/img/blocks/block_footer_tag_line.png Binary files differnew file mode 100755 index 00000000..73b5a29e --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_footer_tag_line.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_header_browser.png b/addons/mass_mailing/static/src/img/blocks/block_header_browser.png Binary files differnew file mode 100755 index 00000000..c05196e7 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_header_browser.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_header_logo.png b/addons/mass_mailing/static/src/img/blocks/block_header_logo.png Binary files differnew file mode 100755 index 00000000..eae72c93 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_header_logo.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_header_social.png b/addons/mass_mailing/static/src/img/blocks/block_header_social.png Binary files differnew file mode 100755 index 00000000..43aaec4c --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_header_social.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_header_text_social.png b/addons/mass_mailing/static/src/img/blocks/block_header_text_social.png Binary files differnew file mode 100755 index 00000000..61bb97fd --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_header_text_social.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_image.png b/addons/mass_mailing/static/src/img/blocks/block_image.png Binary files differnew file mode 100755 index 00000000..e918a8d7 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_image.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_image_text.png b/addons/mass_mailing/static/src/img/blocks/block_image_text.png Binary files differnew file mode 100755 index 00000000..de9e18f3 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_image_text.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_paragraph.png b/addons/mass_mailing/static/src/img/blocks/block_paragraph.png Binary files differnew file mode 100755 index 00000000..bfba996c --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_paragraph.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_steps.png b/addons/mass_mailing/static/src/img/blocks/block_steps.png Binary files differnew file mode 100755 index 00000000..e977a105 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_steps.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_text_image.png b/addons/mass_mailing/static/src/img/blocks/block_text_image.png Binary files differnew file mode 100755 index 00000000..c0832ebd --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_text_image.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_three_cols.png b/addons/mass_mailing/static/src/img/blocks/block_three_cols.png Binary files differnew file mode 100755 index 00000000..cd72e7b8 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_three_cols.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_title_sub.png b/addons/mass_mailing/static/src/img/blocks/block_title_sub.png Binary files differnew file mode 100755 index 00000000..1320ccc5 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_title_sub.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_title_text.png b/addons/mass_mailing/static/src/img/blocks/block_title_text.png Binary files differnew file mode 100755 index 00000000..399f13c2 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_title_text.png diff --git a/addons/mass_mailing/static/src/img/blocks/block_two_cols.png b/addons/mass_mailing/static/src/img/blocks/block_two_cols.png Binary files differnew file mode 100755 index 00000000..79e19de0 --- /dev/null +++ b/addons/mass_mailing/static/src/img/blocks/block_two_cols.png diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_banner.jpg b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_banner.jpg Binary files differnew file mode 100644 index 00000000..872366f9 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_banner.jpg diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_event.jpg b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_event.jpg Binary files differnew file mode 100644 index 00000000..50a9d033 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_event.jpg diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_image.jpg b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_image.jpg Binary files differnew file mode 100644 index 00000000..047516fe --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_image.jpg diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_image_text.jpg b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_image_text.jpg Binary files differnew file mode 100644 index 00000000..59a1806d --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_image_text.jpg diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_text_image.jpg b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_text_image.jpg Binary files differnew file mode 100644 index 00000000..0009598d --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_text_image.jpg diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_three_cols_1.jpg b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_three_cols_1.jpg Binary files differnew file mode 100644 index 00000000..7a7f2bbd --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_three_cols_1.jpg diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_three_cols_2.jpg b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_three_cols_2.jpg Binary files differnew file mode 100644 index 00000000..7a7f2bbd --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_three_cols_2.jpg diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_three_cols_3.jpg b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_three_cols_3.jpg Binary files differnew file mode 100644 index 00000000..7a7f2bbd --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_three_cols_3.jpg diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_two_cols_1.jpg b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_two_cols_1.jpg Binary files differnew file mode 100644 index 00000000..98ed24c6 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_two_cols_1.jpg diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_two_cols_2.jpg b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_two_cols_2.jpg Binary files differnew file mode 100644 index 00000000..98ed24c6 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_block_two_cols_2.jpg diff --git a/addons/mass_mailing/static/src/img/theme_basic/s_default_image_logo.png b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_logo.png Binary files differnew file mode 100644 index 00000000..064f09c1 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_basic/s_default_image_logo.png diff --git a/addons/mass_mailing/static/src/img/theme_default/demo/signature.png b/addons/mass_mailing/static/src/img/theme_default/demo/signature.png Binary files differnew file mode 100644 index 00000000..36cb2642 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/demo/signature.png diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_banner.jpg b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_banner.jpg Binary files differnew file mode 100755 index 00000000..e6a85a38 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_banner.jpg diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_event.jpg b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_event.jpg Binary files differnew file mode 100755 index 00000000..91fff96b --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_event.jpg diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_image.jpg b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_image.jpg Binary files differnew file mode 100755 index 00000000..3ca5ebb5 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_image.jpg diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_image_text.jpg b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_image_text.jpg Binary files differnew file mode 100755 index 00000000..87251f5f --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_image_text.jpg diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_text_image.jpg b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_text_image.jpg Binary files differnew file mode 100755 index 00000000..10f6102f --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_text_image.jpg diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_1.jpg b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_1.jpg Binary files differnew file mode 100755 index 00000000..fa939c89 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_1.jpg diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_2.jpg b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_2.jpg Binary files differnew file mode 100755 index 00000000..35a8006b --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_2.jpg diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_3.jpg b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_3.jpg Binary files differnew file mode 100755 index 00000000..e33e6679 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_3.jpg diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_two_cols_1.jpg b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_two_cols_1.jpg Binary files differnew file mode 100755 index 00000000..9a55e535 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_two_cols_1.jpg diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_two_cols_2.jpg b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_two_cols_2.jpg Binary files differnew file mode 100755 index 00000000..38c77b3c --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_block_two_cols_2.jpg diff --git a/addons/mass_mailing/static/src/img/theme_default/s_default_image_logo.png b/addons/mass_mailing/static/src/img/theme_default/s_default_image_logo.png Binary files differnew file mode 100755 index 00000000..1f5169a2 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_default/s_default_image_logo.png diff --git a/addons/mass_mailing/static/src/img/theme_imgs/basic_thumb_large.png b/addons/mass_mailing/static/src/img/theme_imgs/basic_thumb_large.png Binary files differnew file mode 100644 index 00000000..380661de --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_imgs/basic_thumb_large.png diff --git a/addons/mass_mailing/static/src/img/theme_imgs/basic_thumb_logo.png b/addons/mass_mailing/static/src/img/theme_imgs/basic_thumb_logo.png Binary files differnew file mode 100644 index 00000000..2a562249 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_imgs/basic_thumb_logo.png diff --git a/addons/mass_mailing/static/src/img/theme_imgs/basic_thumb_small.png b/addons/mass_mailing/static/src/img/theme_imgs/basic_thumb_small.png Binary files differnew file mode 100644 index 00000000..32b14ab1 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_imgs/basic_thumb_small.png diff --git a/addons/mass_mailing/static/src/img/theme_imgs/default_thumb_large.png b/addons/mass_mailing/static/src/img/theme_imgs/default_thumb_large.png Binary files differnew file mode 100644 index 00000000..d93ecd63 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_imgs/default_thumb_large.png diff --git a/addons/mass_mailing/static/src/img/theme_imgs/default_thumb_logo.png b/addons/mass_mailing/static/src/img/theme_imgs/default_thumb_logo.png Binary files differnew file mode 100644 index 00000000..157ab855 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_imgs/default_thumb_logo.png diff --git a/addons/mass_mailing/static/src/img/theme_imgs/default_thumb_small.png b/addons/mass_mailing/static/src/img/theme_imgs/default_thumb_small.png Binary files differnew file mode 100644 index 00000000..5c851797 --- /dev/null +++ b/addons/mass_mailing/static/src/img/theme_imgs/default_thumb_small.png diff --git a/addons/mass_mailing/static/src/js/mass_mailing.js b/addons/mass_mailing/static/src/js/mass_mailing.js new file mode 100644 index 00000000..e74d97f5 --- /dev/null +++ b/addons/mass_mailing/static/src/js/mass_mailing.js @@ -0,0 +1,15 @@ +odoo.define('mass_mailing.mass_mailing', function (require) { +"use strict"; + +var KanbanColumn = require('web.KanbanColumn'); + +KanbanColumn.include({ + init: function () { + this._super.apply(this, arguments); + if (this.modelName === 'mailing.mailing') { + this.draggable = false; + } + }, +}); + +}); diff --git a/addons/mass_mailing/static/src/js/mass_mailing_link_dialog_fix.js b/addons/mass_mailing/static/src/js/mass_mailing_link_dialog_fix.js new file mode 100644 index 00000000..6b46340b --- /dev/null +++ b/addons/mass_mailing/static/src/js/mass_mailing_link_dialog_fix.js @@ -0,0 +1,75 @@ + +odoo.define('mass_mailing.fix.LinkDialog', function (require) { +'use strict'; + +const LinkDialog = require('wysiwyg.widgets.LinkDialog'); + +/** + * Primary and link buttons are "hacked" by mailing themes scss. We thus + * have to fix their preview if possible. + */ +LinkDialog.include({ + /** + * @override + */ + start() { + const ret = this._super(...arguments); + if (!$(this.editable).find('.o_mail_wrapper').length) { + return ret; + } + + this.opened().then(() => { + // Ugly hack to show the real color for link and primary which + // depend on the mailing themes. Note: the hack is not enough as + // the mailing theme changes those colors in some environment, + // sometimes (for example 'btn-primary in this snippet looks like + // that')... we'll consider this a limitation until a master + // refactoring of those mailing themes. + this.__realMMColors = {}; + const $previewArea = $('<div/>').addClass('o_mail_snippet_general'); + $(this.editable).find('.o_layout').append($previewArea); + _.each(['link', 'primary', 'secondary'], type => { + const $el = $('<a href="#" class="btn btn-' + type + '"/>'); + $el.appendTo($previewArea); + this.__realMMColors[type] = { + 'border-color': $el.css('border-top-color'), + 'background-color': $el.css('background-color'), + 'color': $el.css('color'), + }; + $el.remove(); + + this.$('.form-group .o_btn_preview.btn-' + type) + .css(_.pick(this.__realMMColors[type], 'background-color', 'color')); + }); + $previewArea.remove(); + + this._adaptPreview(); + }); + + return ret; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _adaptPreview() { + this._super(...arguments); + if (this.__realMMColors) { + var $preview = this.$("#link-preview"); + $preview.css('border-color', ''); + $preview.css('background-color', ''); + $preview.css('color', ''); + _.each(['link', 'primary', 'secondary'], type => { + if ($preview.hasClass('btn-' + type) || type === 'link' && !$preview.hasClass('btn')) { + $preview.css(this.__realMMColors[type]); + } + }); + } + }, +}); + +}); diff --git a/addons/mass_mailing/static/src/js/mass_mailing_list_kanban_record.js b/addons/mass_mailing/static/src/js/mass_mailing_list_kanban_record.js new file mode 100644 index 00000000..52d7076b --- /dev/null +++ b/addons/mass_mailing/static/src/js/mass_mailing_list_kanban_record.js @@ -0,0 +1,18 @@ +odoo.define('mass_mailing.ListKanbanRecord', function (require) { +"use strict"; + +var KanbanRecord = require('web.KanbanRecord'); + +var MassMailingListKanbanRecord = KanbanRecord.extend({ + /** + * @override + * @private + */ + _openRecord: function () { + this.$('.o_mailing_list_kanban_boxes a').first().click(); + } +}); + +return MassMailingListKanbanRecord; + +}); diff --git a/addons/mass_mailing/static/src/js/mass_mailing_list_kanban_renderer.js b/addons/mass_mailing/static/src/js/mass_mailing_list_kanban_renderer.js new file mode 100644 index 00000000..b9804e72 --- /dev/null +++ b/addons/mass_mailing/static/src/js/mass_mailing_list_kanban_renderer.js @@ -0,0 +1,16 @@ +odoo.define('mass_mailing.ListKanbanRenderer', function (require) { +"use strict"; + +var MassMailingListKanbanRecord = require('mass_mailing.ListKanbanRecord'); + +var KanbanRenderer = require('web.KanbanRenderer'); + +var MassMailingListKanbanRenderer = KanbanRenderer.extend({ + config: _.extend({}, KanbanRenderer.prototype.config, { + KanbanRecord: MassMailingListKanbanRecord, + }) +}); + +return MassMailingListKanbanRenderer; + +}); diff --git a/addons/mass_mailing/static/src/js/mass_mailing_list_kanban_view.js b/addons/mass_mailing/static/src/js/mass_mailing_list_kanban_view.js new file mode 100644 index 00000000..86bac695 --- /dev/null +++ b/addons/mass_mailing/static/src/js/mass_mailing_list_kanban_view.js @@ -0,0 +1,19 @@ +odoo.define('mass_mailing.ListKanbanView', function (require) { +"use strict"; + +var MassMailingListKanbanRenderer = require('mass_mailing.ListKanbanRenderer'); + +var KanbanView = require('web.KanbanView'); +var view_registry = require('web.view_registry'); + +var MassMailingListKanbanView = KanbanView.extend({ + config: _.extend({}, KanbanView.prototype.config, { + Renderer: MassMailingListKanbanRenderer, + }), +}); + +view_registry.add('mass_mailing_list_kanban', MassMailingListKanbanView); + +return MassMailingListKanbanView; + +}); diff --git a/addons/mass_mailing/static/src/js/mass_mailing_snippets.js b/addons/mass_mailing/static/src/js/mass_mailing_snippets.js new file mode 100644 index 00000000..ef4bd007 --- /dev/null +++ b/addons/mass_mailing/static/src/js/mass_mailing_snippets.js @@ -0,0 +1,153 @@ +odoo.define('mass_mailing.snippets.options', function (require) { +"use strict"; + +var options = require('web_editor.snippets.options'); + +// Snippet option for resizing image and column width inline like excel +options.registry.mass_mailing_sizing_x = options.Class.extend({ + /** + * @override + */ + start: function () { + var def = this._super.apply(this, arguments); + + this.containerWidth = this.$target.parent().closest("td, table, div").width(); + + var self = this; + var offset, sib_offset, target_width, sib_width; + + this.$overlay.find(".o_handle.e, .o_handle.w").removeClass("readonly"); + this.isIMG = this.$target.is("img"); + if (this.isIMG) { + this.$overlay.find(".o_handle.w").addClass("readonly"); + } + + var $body = $(this.ownerDocument.body); + this.$overlay.find(".o_handle").on('mousedown', function (event) { + event.preventDefault(); + var $handle = $(this); + var compass = false; + + _.each(['n', 's', 'e', 'w'], function (handler) { + if ($handle.hasClass(handler)) { compass = handler; } + }); + if (self.isIMG) { compass = "image"; } + + $body.on("mousemove.mass_mailing_width_x", function (event) { + event.preventDefault(); + offset = self.$target.offset().left; + target_width = self.get_max_width(self.$target); + if (compass === 'e' && self.$target.next().offset()) { + sib_width = self.get_max_width(self.$target.next()); + sib_offset = self.$target.next().offset().left; + self.change_width(event, self.$target, target_width, offset, true); + self.change_width(event, self.$target.next(), sib_width, sib_offset, false); + } + if (compass === 'w' && self.$target.prev().offset()) { + sib_width = self.get_max_width(self.$target.prev()); + sib_offset = self.$target.prev().offset().left; + self.change_width(event, self.$target, target_width, offset, false); + self.change_width(event, self.$target.prev(), sib_width, sib_offset, true); + } + if (compass === 'image') { + self.change_width(event, self.$target, target_width, offset, true); + } + }); + $body.one("mouseup", function () { + $body.off('.mass_mailing_width_x'); + }); + }); + + return def; + }, + change_width: function (event, target, target_width, offset, grow) { + target.css("width", grow ? (event.pageX - offset) : (offset + target_width - event.pageX)); + this.trigger_up('cover_update'); + }, + get_int_width: function (el) { + return parseInt($(el).css("width"), 10); + }, + get_max_width: function ($el) { + return this.containerWidth - _.reduce(_.map($el.siblings(), this.get_int_width), function (memo, w) { return memo + w; }); + }, + onFocus: function () { + this._super.apply(this, arguments); + + if (this.$target.is("td, th")) { + this.$overlay.find(".o_handle.e, .o_handle.w").toggleClass("readonly", this.$target.siblings().length === 0); + } + }, +}); + +options.registry.mass_mailing_table_item = options.Class.extend({ + onClone: function (options) { + this._super.apply(this, arguments); + + // If we cloned a td or th element... + if (options.isCurrent && this.$target.is("td, th")) { + // ... and that the td or th element was alone on its row ... + if (this.$target.siblings().length === 1) { + var $tr = this.$target.parent(); + $tr.clone().empty().insertAfter($tr).append(this.$target); // ... move the clone in a new row instead + return; + } + + // ... if not, if the clone neighbor is an empty cell, remove this empty cell (like if the clone content had been put in that cell) + var $next = this.$target.next(); + if ($next.length && $next.text().trim() === "") { + $next.remove(); + return; + } + + // ... if not, insert an empty col in each other row, at the index of the clone + var width = this.$target.width(); + var $trs = this.$target.closest("table").children("thead, tbody, tfoot").addBack().children("tr").not(this.$target.parent()); + _.each($trs.children(":nth-child(" + this.$target.index() + ")"), function (col) { + $(col).after($("<td/>", {style: "width: " + width + "px;"})); + }); + } + }, + onRemove: function () { + this._super.apply(this, arguments); + + // If we are removing a td or th element which was not alone on its row ... + if (this.$target.is("td, th") && this.$target.siblings().length > 0) { + var $trs = this.$target.closest("table").children("thead, tbody, tfoot").addBack().children("tr").not(this.$target.parent()); + if ($trs.length) { // ... if there are other rows in the table ... + var $last_tds = $trs.children(":last-child"); + if (_.reduce($last_tds, function (memo, td) { return memo + (td.innerHTML || ""); }, "").trim() === "") { + $last_tds.remove(); // ... remove the potential full empty column in the table + } else { + this.$target.parent().append("<td/>"); // ... else, if there is no full empty column, append an empty col in the current row + } + } + } + }, +}); + +// Adding compatibility for the outlook compliance of mailings. +// Commit of such compatibility : a14f89c8663c9cafecb1cc26918055e023ecbe42 +options.registry.BackgroundImage = options.registry.BackgroundImage.extend({ + start: function () { + this._super(); + if (this.snippets && this.snippets.split('.')[0] === "mass_mailing") { + var $table_target = this.$target.find('table:first'); + if ($table_target.length) { + this.$target = $table_target; + } + } + } +}); + +// TODO remove in master when removing the XML div. The option has been disabled +// in 14.0 because of tricky problems to resolve that require refactoring: +// the ability to clean snippet without saving and reloading the page. +options.registry.SnippetSave.include({ + + async saveSnippet(previewMode, widgetValue, params) {}, + + async _computeVisibility() { + return false; + }, +}); +}); 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; + +}); diff --git a/addons/mass_mailing/static/src/js/tours/mass_mailing_tour.js b/addons/mass_mailing/static/src/js/tours/mass_mailing_tour.js new file mode 100644 index 00000000..0004e50e --- /dev/null +++ b/addons/mass_mailing/static/src/js/tours/mass_mailing_tour.js @@ -0,0 +1,88 @@ +odoo.define('mass_mailing.mass_mailing_tour', function (require) { + "use strict"; + + var core = require('web.core'); + var _t = core._t; + var tour = require('web_tour.tour'); + var now = moment(); + + tour.register('mass_mailing_tour', { + url: '/web', + rainbowManMessage: _t('Congratulations, I love your first mailing. :)'), + sequence: 200, + }, [tour.stepUtils.showAppsMenuItem(), { + trigger: '.o_app[data-menu-xmlid="mass_mailing.mass_mailing_menu_root"]', + content: _t("Let's try the Email Marketing app."), + width: 210, + position: 'bottom', + edition: 'enterprise', + }, { + trigger: '.o_app[data-menu-xmlid="mass_mailing.mass_mailing_menu_root"]', + content: _t("Let's try the Email Marketing app."), + edition: 'community', + }, { + trigger: '.o-kanban-button-new', + content: _t("Start by creating your first <b>Mailing</b>."), + position: 'bottom', + }, { + trigger: 'input[name="subject"]', + content: _t('Pick the <b>email subject</b>.'), + position: 'right', + run: 'text ' + now.format("MMMM") + " Newsletter", + }, { + trigger: 'div[name="contact_list_ids"] > .o_input_dropdown > input[type="text"]', + run: 'click', + auto: true, + }, { + trigger: 'li.ui-menu-item', + run: 'click', + auto: true, + }, { + trigger: 'div[name="body_arch"] iframe #newsletter', + content: _t('Choose this <b>theme</b>.'), + position: 'left', + edition: 'enterprise', + run: 'click', + }, { + trigger: 'div[name="body_arch"] iframe #default', + content: _t('Choose this <b>theme</b>.'), + position: 'right', + edition: 'community', + run: 'click', + }, { + trigger: 'div[name="body_arch"] iframe div.o_mail_block_paragraph', + content: _t('Click on this paragraph to edit it.'), + position: 'top', + edition: 'enterprise', + run: 'click', + }, { + trigger: 'div[name="body_arch"] iframe div.o_mail_block_title_text', + content: _t('Click on this paragraph to edit it.'), + position: 'top', + edition: 'community', + run: 'click', + }, { + trigger: 'button[name="action_test"]', + content: _t("Test this mailing by sending a copy to yourself."), + position: 'bottom', + }, { + trigger: 'button[name="send_mail_test"]', + content: _t("Check the email address and click send."), + position: 'bottom', + }, { + trigger: 'button[name="action_put_in_queue"]', + content: _t("Ready for take-off!"), + position: 'bottom', + }, { + trigger: '.btn-primary:contains("Ok")', + content: _t("Don't worry, the mailing contact we created is an internal user."), + position: 'bottom', + run: "click", + }, { + trigger: '.o_back_button', + content: _t("By using the <b>Breadcrumb</b>, you can navigate back to the overview."), + position: 'bottom', + run: 'click', + }] + ); +}); diff --git a/addons/mass_mailing/static/src/js/unsubscribe.js b/addons/mass_mailing/static/src/js/unsubscribe.js new file mode 100644 index 00000000..fae7d16e --- /dev/null +++ b/addons/mass_mailing/static/src/js/unsubscribe.js @@ -0,0 +1,198 @@ +odoo.define('mass_mailing.unsubscribe', function (require) { + 'use strict'; + + var session = require('web.session'); + var ajax = require('web.ajax'); + var core = require('web.core'); + require('web.dom_ready'); + + var _t = core._t; + + var email = $("input[name='email']").val(); + var mailing_id = parseInt($("input[name='mailing_id']").val()); + var res_id = parseInt($("input[name='res_id']").val()); + var token = (location.search.split('token' + '=')[1] || '').split('&')[0]; + + if (!$('.o_unsubscribe_form').length) { + return Promise.reject("DOM doesn't contain '.o_unsubscribe_form'"); + } + session.load_translations().then(function () { + if (email != '' && email != undefined){ + ajax.jsonRpc('/mailing/blacklist/check', 'call', {'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) + .then(function (result) { + if (result == 'unauthorized'){ + $('#button_add_blacklist').hide(); + $('#button_remove_blacklist').hide(); + } + else if (result == true) { + $('#button_remove_blacklist').show(); + toggle_opt_out_section(false); + } + else if (result == false) { + $('#button_add_blacklist').show(); + toggle_opt_out_section(true); + } + else { + $('#subscription_info').html(_t('An error occured. Please try again later or contact us.')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error'); + } + }) + .guardedCatch(function () { + $('#subscription_info').html(_t('An error occured. Please try again later or contact us.')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error'); + }); + } + else { + $('#div_blacklist').hide(); + } + + var unsubscribed_list = $("input[name='unsubscribed_list']").val(); + if (unsubscribed_list){ + $('#subscription_info').html(_.str.sprintf( + _t("You have been <strong>successfully unsubscribed from %s</strong>."), + _.escape(unsubscribed_list) + )); + } + else{ + $('#subscription_info').html(_t('You have been <strong>successfully unsubscribed</strong>.')); + } + }); + + $('#unsubscribe_form').on('submit', function (e) { + e.preventDefault(); + + var checked_ids = []; + $("input[type='checkbox']:checked").each(function (i){ + checked_ids[i] = parseInt($(this).val()); + }); + + var unchecked_ids = []; + $("input[type='checkbox']:not(:checked)").each(function (i){ + unchecked_ids[i] = parseInt($(this).val()); + }); + + ajax.jsonRpc('/mail/mailing/unsubscribe', 'call', {'opt_in_ids': checked_ids, 'opt_out_ids': unchecked_ids, 'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) + .then(function (result) { + if (result == 'unauthorized'){ + $('#subscription_info').html(_t('You are not authorized to do this!')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-error').addClass('alert-warning'); + } + else if (result == true) { + $('#subscription_info').html(_t('Your changes have been saved.')); + $('#info_state').removeClass('alert-info').addClass('alert-success'); + } + else { + $('#subscription_info').html(_t('An error occurred. Your changes have not been saved, try again later.')); + $('#info_state').removeClass('alert-info').addClass('alert-warning'); + } + }) + .guardedCatch(function () { + $('#subscription_info').html(_t('An error occurred. Your changes have not been saved, try again later.')); + $('#info_state').removeClass('alert-info').addClass('alert-warning'); + }); + }); + + // ================== + // Blacklist + // ================== + $('#button_add_blacklist').click(function (e) { + e.preventDefault(); + + ajax.jsonRpc('/mailing/blacklist/add', 'call', {'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) + .then(function (result) { + if (result == 'unauthorized'){ + $('#subscription_info').html(_t('You are not authorized to do this!')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-error').addClass('alert-warning'); + } + else + { + if (result) { + $('#subscription_info').html(_t('You have been successfully <strong>added to our blacklist</strong>. ' + + 'You will not be contacted anymore by our services.')); + $('#info_state').removeClass('alert-warning').removeClass('alert-info').removeClass('alert-error').addClass('alert-success'); + toggle_opt_out_section(false); + } + else { + $('#subscription_info').html(_t('An error occured. Please try again later or contact us.')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error'); + } + $('#button_add_blacklist').hide(); + $('#button_remove_blacklist').show(); + $('#unsubscribed_info').hide(); + } + }) + .guardedCatch(function () { + $('#subscription_info').html(_t('An error occured. Please try again later or contact us.')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error'); + }); + }); + + $('#button_remove_blacklist').click(function (e) { + e.preventDefault(); + + ajax.jsonRpc('/mailing/blacklist/remove', 'call', {'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) + .then(function (result) { + if (result == 'unauthorized'){ + $('#subscription_info').html(_t('You are not authorized to do this!')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-error').addClass('alert-warning'); + } + else + { + if (result) { + $('#subscription_info').html(_t("You have been successfully <strong>removed from our blacklist</strong>. " + + "You are now able to be contacted by our services.")); + $('#info_state').removeClass('alert-warning').removeClass('alert-info').removeClass('alert-error').addClass('alert-success'); + toggle_opt_out_section(true); + } + else { + $('#subscription_info').html(_t('An error occured. Please try again later or contact us.')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error'); + } + $('#button_add_blacklist').show(); + $('#button_remove_blacklist').hide(); + $('#unsubscribed_info').hide(); + } + }) + .guardedCatch(function () { + $('#subscription_info').html(_t('An error occured. Please try again later or contact us.')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error'); + }); + }); + + // ================== + // Feedback + // ================== + $('#button_feedback').click(function (e) { + var feedback = $("textarea[name='opt_out_feedback']").val(); + e.preventDefault(); + ajax.jsonRpc('/mailing/feedback', 'call', {'mailing_id': mailing_id, 'res_id': res_id, 'email': email, 'feedback': feedback, 'token': token}) + .then(function (result) { + if (result == 'unauthorized'){ + $('#subscription_info').html(_t('You are not authorized to do this!')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-error').addClass('alert-warning'); + } + else if (result == true){ + $('#subscription_info').html(_t('Thank you! Your feedback has been sent successfully!')); + $('#info_state').removeClass('alert-warning').removeClass('alert-info').removeClass('alert-error').addClass('alert-success'); + $("#div_feedback").hide(); + } + else { + $('#subscription_info').html(_t('An error occured. Please try again later or contact us.')); + $('#info_state').removeClass('alert-success').removeClass('alert-info').removeClass('alert-error').addClass('alert-warning'); + } + }) + .guardedCatch(function () { + $('#subscription_info').html(_t('An error occured. Please try again later or contact us.')); + $('#info_state').removeClass('alert-info').removeClass('alert-success').removeClass('alert-error').addClass('alert-warning'); + }); + }); +}); + +function toggle_opt_out_section(value) { + var result = !value; + $("#div_opt_out").find('*').attr('disabled',result); + $("#button_add_blacklist").attr('disabled', false); + $("#button_remove_blacklist").attr('disabled', false); + if (value) { $('[name="button_subscription"]').addClass('clickable'); } + else { $('[name="button_subscription"]').removeClass('clickable'); } +} diff --git a/addons/mass_mailing/static/src/scss/mass_mailing.scss b/addons/mass_mailing/static/src/scss/mass_mailing.scss new file mode 100644 index 00000000..5b8ebff2 --- /dev/null +++ b/addons/mass_mailing/static/src/scss/mass_mailing.scss @@ -0,0 +1,50 @@ +.o_kanban_view { + .oe_kanban_mass_mailing { + .o_title { + margin-bottom: 16px; + } + .o_kanban_primary_bottom { + margin-top: 16px; + } + .oe_margin_top_8 { + margin-top: 8px; + } + .oe_margin_bottom_8 { + margin-bottom: 8px; + } + } +} +.o_form_view { + // This will display the emoji widget in the right position after a text field with sms option. + .o_sms_container ~ .o_mail_add_emoji{ + bottom: 55px; + } +} +.o_white_body { + background-color: white; +} + +.o_mass_mailing_unsubscribed { + margin-left: 20px; + color: #005326; + font-size: 90%; +} + +@media only screen and (min-width: 1200px) { + .o_utm_campaign_mass_mailing_substats { + padding-right: 210px; + div { + margin-left: 50px; + } + } +} + +@media only screen and (min-width: 768px) and (max-width: 1200px) { + .o_utm_campaign_mass_mailing_substats { + padding-right: 180px; + div { + margin-left: 30px; + } + } +} + diff --git a/addons/mass_mailing/static/src/scss/mass_mailing.ui.jw.scss b/addons/mass_mailing/static/src/scss/mass_mailing.ui.jw.scss new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/addons/mass_mailing/static/src/scss/mass_mailing.ui.jw.scss diff --git a/addons/mass_mailing/static/src/scss/mass_mailing.ui.scss b/addons/mass_mailing/static/src/scss/mass_mailing.ui.scss new file mode 100644 index 00000000..fea99064 --- /dev/null +++ b/addons/mass_mailing/static/src/scss/mass_mailing.ui.scss @@ -0,0 +1,206 @@ +.o_form_view .o_form_sheet .o_notebook .tab-content .tab-pane .o_mail_body { + // cancel padding of form_sheet + margin-top: -$o-sheet-cancel-tpadding; + margin-left: -$o-sheet-cancel-hpadding; + margin-right: -$o-sheet-cancel-hpadding; + margin-bottom: -$o-sheet-cancel-bpadding; +} + +.o_mail_theme_selector { + > a { + @include o-position-absolute(0, 0, auto, 0); + height: $o-we-toolbar-height; + line-height: $o-we-toolbar-height; + border-radius: 0; + background-color: $o-we-sidebar-bg; + color: #212629; + display: flex; + justify-content: center; + align-items: center; + box-shadow: none !important; + + &:hover, &:focus, &:active { + color: #4e525b; + } + + i { + margin-right: 10px; + } + } + + &.show > a { + color: white; + background-color: #212629; + } + + .dropdown-menu { + position: absolute; + bottom: -2px; + top: -3px !important; + left: -5px !important; + right: -2px !important; + margin: 0; + border-radius: 0; + overflow: auto; + background-color: $o-we-sidebar-bg; + z-index: 1050; + + &.show { + bottom: 31px !important; + top: -2px !important; + right: 3px !important; + } + + .dropdown-item { + padding: 10px 10px; + &:first-child { + display: none; + } + + .o_thumb { + display: none; + background-size: cover; + padding-top: 50%; + border: 1px solid $o-we-border-color; + + + &.logo { + display: block; + } + } + + + &:hover { + background-color: $o-we-sidebar-bg; + + .o_thumb { + border: 1px solid black; + } + } + + &.selected .o_thumb { + border: 2px solid $o-brand-odoo; + background-color: $o-we-sidebar-bg; + } + } + } +} + +body.o_force_mail_theme_choice { + #oe_snippets { + width: 100%; + + .o_mail_theme_selector { + .dropdown-toggle { + display: none; + } + + .dropdown-menu { + display: block; + + .dropdown-item { + margin: 0; + float: left; + clear: none; + width: 100%; + max-width: 25%; + transition: all 0.3s ease 0s; + + &:first-child { + display: block; + } + + .o_thumb { + display: none; + padding-top: 107%; + border: 1px solid #4e525b; + border-top: 1px solid $o-we-border-color; + box-shadow: 0 5px 10px rgba(black, 0.8); + will-change: transform; + backface-visibility: hidden; + transition: all 0.3s ease 0s; + + &.small { + display: block; + } + + @media screen and (min-width: 900px) { + &.small { + display: none; + } + &.large { + display: block; + } + } + } + + &:hover { + background-color: #212629; + + .o_thumb { + box-shadow: 0 5px 30px 1px rgba(black, 0.6); + } + } + + &.o_mass_mailing_themes_upgrade .o_thumb { + position: relative; + display: block; + border: 1px dashed white; + opacity: 0.2; + + > .fa { + @include o-position-absolute(0, 0, 0, 0); + text-align: center; + font-size: 50px; + color: white; + + &::before { + vertical-align: middle; + } + &::after { + content: ""; + display: inline-block; + height: 100%; + vertical-align: middle; + } + } + } + } + } + } + } + .note-editor { + display: none; + } +} + +body.editor_enable.o_basic_theme.o_in_iframe { + padding-right: 0px !important; + + #web_editor-top-edit .note-popover .popover { + right: 0 !important; + } + + #oe_snippets { + display: none; + } +} + +.note-editable .o_layout { + overflow: initial; +} + +#web_editor-toolbars .popover .note-color { + .note-back-color-preview .dropdown-toggle, + .note-fore-color-preview .dropdown-toggle { + height: $o-we-toolbar-height - 1; + } +} + +.oe_structure { + width: 100%; +} + +:root { + font-size: 14px; +} diff --git a/addons/mass_mailing/static/src/scss/mass_mailing.ui.shadow.scss b/addons/mass_mailing/static/src/scss/mass_mailing.ui.shadow.scss new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/addons/mass_mailing/static/src/scss/mass_mailing.ui.shadow.scss diff --git a/addons/mass_mailing/static/src/scss/mass_mailing_mobile.scss b/addons/mass_mailing/static/src/scss/mass_mailing_mobile.scss new file mode 100644 index 00000000..9df45859 --- /dev/null +++ b/addons/mass_mailing/static/src/scss/mass_mailing_mobile.scss @@ -0,0 +1,7 @@ +@include media-breakpoint-down(sm) { + .o_utm_campaign_mass_mailing_substats { + width: 80%; + margin: 0 auto; + justify-content: space-between !important; + } +}
\ No newline at end of file diff --git a/addons/mass_mailing/static/src/scss/themes/theme_basic.scss b/addons/mass_mailing/static/src/scss/themes/theme_basic.scss new file mode 100644 index 00000000..d65ea077 --- /dev/null +++ b/addons/mass_mailing/static/src/scss/themes/theme_basic.scss @@ -0,0 +1,54 @@ + +// TODO remove in master +// Themes scss have to use bg-variant and text-emphasis-variant mixins of BS4 +// to properly work. That use was introduced as a fix in 12.0... but the BS4 +// mixins are unavailable in the mailing assets. As a stable fix cannot +// introduce static code which requires an XML update to not crash, the mixins +// were duplicated here instead. +@mixin bg-variant($parent, $color) { + #{$parent} { + background-color: $color !important; + } + a#{$parent}, + button#{$parent} { + &:hover, &:focus { + background-color: darken($color, 10%) !important; + } + } +} +@mixin text-emphasis-variant($parent, $color) { + #{$parent} { + color: $color !important; + } + a#{$parent} { + &:hover, &:focus { + color: darken($color, 10%) !important; + } + } +} + +// ============================ +// Mass Mailing "Theme Basic" +// ============================ + +// ===== Layout ===== +.o_basic_theme { + &.o_layout { + margin: 0; + padding: 0; + background-color: white; + + &, p, h1, h2, h3, h4, h5, h6, span, ul, ol { + color: black; + } + + p { + font-size: 13px; + margin-bottom: 0px; + } + + ul, ol { + margin: 0; + } + } +} diff --git a/addons/mass_mailing/static/src/scss/themes/theme_default.scss b/addons/mass_mailing/static/src/scss/themes/theme_default.scss new file mode 100644 index 00000000..96e98648 --- /dev/null +++ b/addons/mass_mailing/static/src/scss/themes/theme_default.scss @@ -0,0 +1,307 @@ +// ============================ +// Mass Mailing "Theme Default" +// ============================ + +// ===== Default Theme palette ===== +$o-mm-def-color-1: #f5f5f5; +$o-mm-def-color-2: #ffffff; +$o-mm-def-color-3: #706482; +$o-mm-def-color-4: #464646; +$o-mm-def-color-5: darken($o-mm-def-color-3, 5%); +$o-mm-def-color-6: #706482; +$o-mm-def-color-7: #87a6b5; + +// ===== Default Theme variables ===== +$o-mm-def-body-width : 600px; +$o-mm-def-body-bobile : 480px; +$o-mm-def-b-radius : 2px; +$o-mm-def-body-bg : $o-mm-def-color-2; + +$o-mm-def-font : -apple-system, "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; +$o-mm-def-text-color : $o-mm-def-color-4; + +$o-mm-def-btn-bg : $o-mm-def-color-3; +$o-mm-def-btn-text : $o-mm-def-color-2; + +// ===== Colors ===== +@include bg-variant(".bg-o-color-2", $o-mm-def-color-6); +@include text-emphasis-variant(".text-o-color-2", $o-mm-def-color-6); +@include bg-variant(".bg-o-color-4", $o-mm-def-color-7); +@include bg-variant(".bg-gray-lighter", $o-mm-def-color-1); + +td:not([align]) { + // Default browser style but needed so that alignment works on some mail + // clients (see convert_inline) + text-align: inherit; +} + +// ===== Layout ===== +.o_layout { + overflow: hidden; + box-sizing: content-box; + width: 94%; + min-height: 100%; + background-color: $o-mm-def-color-1; + padding: 0 3%; + + // Forces on <p/> and <hx/> elements as several mail clients does not + // correctly inherit font properties + &, p, ol { + font-size: 14px; // force bootstrap default + } + &, p, h1, h2, h3, h4, h5, h6, ol { + font-family: $o-mm-def-font; + color: lighten($o-mm-def-text-color, 20%); + } + + > .o_mail_wrapper { + width: 100%; + border-collapse: separate; // Allow the first and last td to have the + // same width + + .o_mail_wrapper_td { + width: $o-mm-def-body-width; + border: 1px solid darken($o-mm-def-color-1, 2%); + background-color: $o-mm-def-color-2; + } + } +} + +// ===== Snippets (general) ===== +.o_mail_snippet_general { + width: 100%; + max-width: $o-mm-def-body-width; // should not be necessary thanks to mail wrapper + margin: 0 auto; + + h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { + color: $o-mm-def-text-color; + } + + .btn.btn-primary { + color: $o-mm-def-btn-text; + border: none; + text-decoration: none; + font-weight: normal; + font-family: $o-mm-def-font; + cursor: pointer; + border-radius: $o-mm-def-b-radius; + background-color: $o-mm-def-btn-bg; + } + + a:not(.btn), .btn.btn-link { + color: $o-mm-def-color-5; + font-weight: bold; + text-decoration: none !important; + + &:hover, &:focus, &:active { + text-decoration: none !important; + } + } + + img { + width: 100%; + max-width: 100%; + height: auto; + } + + .separator { + height: 2px; + background-color: $o-mm-def-color-1; + } + + .bg-o-color-2 { + h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { + color: $o-mm-def-color-2; + } + + .btn.btn-primary { + background-color: $o-mm-def-color-7; + } + + a:not(.btn), .btn.btn-link { + color: $o-mm-def-color-7; + } + } + + td, th { + vertical-align: top; + } +} + +// ===== Snippets (specific) ===== +.o_mail_block_header_social, .o_mail_block_header_text_social, .o_mail_block_header_logo { + .o_mail_logo_container { + img { + width: auto; + } + } + .o_mail_header_social { + margin-right: 20px; + + a:not(.btn), .btn.btn-link { + display: inline-block; + } + } + td { + vertical-align: middle; + } +} + +.o_mail_block_header_text_social { + h3 { + font-weight: bold; + } +} + +.o_mail_block_steps .o_mail_snippet_general{ + background-color: $o-mm-def-color-1; +} + +.o_mail_block_two_cols, .o_mail_block_three_cols { + // Forces on <p/> elements as several mail clients does not correctly + // inherit font properties + &, p { + font-size: small; + } + + .o_mail_col_container { + padding: 10px 20px; + + h4 { + margin-top: 15px; + } + } +} + +.o_mail_block_image_text, .o_mail_block_text_image { + // Forces on <p/> elements as several mail clients does not correctly + // inherit font properties + &, p { + font-size: small; + } +} + +.o_mail_block_event { + margin-left: 20px; + margin-right: 20px; + .o_mail_snippet_general{ + background: darken($o-mm-def-color-1, 2%); + } +} + +.o_mail_block_footer_separator { + margin-left: 20px; + margin-right: 20px; +} + +.o_mail_block_comparison_table { + .separator { + height: 1px; + background-color: darken($o-mm-def-color-1, 15%); + } + table table { + td, th { + vertical-align: middle; + } + td { + padding: 10px 20px; + * { + margin: 0; + line-height: 1.3; + } + } + } +} + +.o_mail_block_discount2 { + .o_code { + padding: 5px 10px; + background: $o-mm-def-color-1; + border-radius: 3px; + font-family: monospace; + } +} + +.o_mail_block_footer_social { + .o_mail_table_styles { + border-top: 2px solid $o-mm-def-color-1; + padding-top: 20px; + padding-bottom: 20px; + } + + .o_mail_footer_links { + padding-top: 10px; + padding-bottom: 10px; + + .btn-link{ + padding: 0; + font-size: 12px; + } + } + + .o_mail_footer_copy { + font-size: 9px; + font-weight: bold; + color: lighten($o-mm-def-text-color, 30%); + } + + &.o_mail_footer_social_center { + .o_mail_footer_social, .o_mail_footer_links, .o_mail_footer_copy { + text-align: center; + } + } + + &.o_mail_footer_social_left { + .o_mail_footer_description { + padding-left: 20px; + } + .o_mail_footer_social { + padding-right: 20px; + text-align: right; + vertical-align: top; + + .btn-link{ + padding: 0; + font-size: 12px; + } + } + } + td { + vertical-align: middle; + } +} + +.o_mail_display_coupon { + font-size: 50px; + line-height: 1; +} + +// ===== Misc ===== +.o_mail_h_padding { + padding-left: 20px; + padding-right: 20px; +} +.o_mail_v_padding { + padding-top: 20px; + padding-bottom: 20px; +} +.o_mail_no_margin { + margin: 0; + + &.o_mail_display_coupon { + line-height: 1.1; + } +} +.o_mail_table_styles { + width: 100%; + border-collapse:separate; +} + +// Compatibility: mb/mt classes on <td/> elements were interpreted as paddings +// instead of margins before the introduction of pb/pt classes. As thoses mb/mt +// classes may remain on clients' templates, <td/> are forced to 0 margin to +// be sure (normally <td/> should not consider margin properties but some mail +// clients might make the mistake to do so). +td[class*="mb"], td[class*="mt"] { + margin: 0!important; +} diff --git a/addons/mass_mailing/static/src/xml/mass_mailing.xml b/addons/mass_mailing/static/src/xml/mass_mailing.xml new file mode 100644 index 00000000..b4a3bfce --- /dev/null +++ b/addons/mass_mailing/static/src/xml/mass_mailing.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> + <div t-name="mass_mailing.theme_selector" class="o_mail_theme_selector"> + <a role="button" href="#" class="btn btn-sm dropdown-toggle" data-toggle="dropdown"> + <i class="fa fa-paint-brush"/> Change Style + </a> + <div class="dropdown-menu" role="menu"> + <t t-foreach="themes" t-as="theme"> + <a t-att-id="theme.name" role="menuitem" href="#" class="dropdown-item"> + <div class="o_thumb small" t-attf-style="background-image: url(#{theme.img}_small.png)"/> + <div class="o_thumb large" t-attf-style="background-image: url(#{theme.img}_large.png)"/> + <div class="o_thumb logo" t-attf-style="background-image: url(#{theme.img}_logo.png)"/> + </a> + </t> + <t t-if="themes.length === 1"> + <a role="menuitem" href="#" class="dropdown-item o_mass_mailing_themes_upgrade"> + <div class="o_thumb"><i class="fa fa-plus" role="img" aria-label="Upgrade theme" title="Upgrade theme"/></div> + </a> + </t> + </div> + </div> +</templates> |
