diff --git a/addons/hr_recruitment/wizard/applicant_send_mail.py b/addons/hr_recruitment/wizard/applicant_send_mail.py index de3fc005851..79e6d884d4b 100644 --- a/addons/hr_recruitment/wizard/applicant_send_mail.py +++ b/addons/hr_recruitment/wizard/applicant_send_mail.py @@ -41,10 +41,10 @@ class ApplicantSendMail(models.TransientModel): }) applicant.message_post( - subject=self.subject, body=self.body, - message_type='comment', email_from=self.author_id.email, email_layout_xmlid='mail.mail_notification_light', + message_type='comment', partner_ids=applicant.partner_id.ids, + subject=self.subject, ) diff --git a/addons/mail/models/mail_render_mixin.py b/addons/mail/models/mail_render_mixin.py index 39874b7b25c..8ad6fe4a913 100644 --- a/addons/mail/models/mail_render_mixin.py +++ b/addons/mail/models/mail_render_mixin.py @@ -13,7 +13,7 @@ from werkzeug import urls from odoo import _, api, fields, models, tools from odoo.addons.base.models.ir_qweb import QWebException from odoo.exceptions import UserError, AccessError -from odoo.tools import is_html_empty, safe_eval +from odoo.tools import is_html_empty from odoo.tools.rendering_tools import convert_inline_template_to_qweb, parse_inline_template, render_inline_template, template_env_globals _logger = logging.getLogger(__name__) @@ -273,6 +273,10 @@ class MailRenderMixin(models.AbstractModel): add_context=None, options=None): """ Render a raw QWeb template. + In addition to the generic evaluation context available, some other + variables are added: + * ``object``: record based on which the template is rendered; + :param str template_src: raw QWeb template to render; :param str model: see ``MailRenderMixin._render_template()``; :param list res_ids: see ``MailRenderMixin._render_template()``; @@ -283,8 +287,6 @@ class MailRenderMixin(models.AbstractModel): :param dict options: options for rendering (not used currently); :return dict: {res_id: string of rendered template based on record} - - :notice: Experimental. Use at your own risks only. """ results = dict.fromkeys(res_ids, u"") if not template_src: @@ -473,6 +475,7 @@ class MailRenderMixin(models.AbstractModel): else: rendered = self._render_template_inline_template(template_src, model, res_ids, add_context=add_context, options=options) + if post_process: rendered = self._render_template_postprocess(rendered) @@ -543,6 +546,7 @@ class MailRenderMixin(models.AbstractModel): :param string set_lang: force language for rendering. It should be a valid lang code matching an activate res.lang. Checked only if ``compute_lang`` is False; + :param dict add_context: additional context to give to renderer; :param dict options: options for rendering; :param boolean post_process: perform a post processing on rendered result @@ -570,7 +574,12 @@ class MailRenderMixin(models.AbstractModel): (res_id, rendered) for lang, (template, tpl_res_ids) in templates_res_ids.items() for res_id, rendered in template._render_template( - template[field], template.render_model, tpl_res_ids, engine=engine, - add_context=add_context, options=options, post_process=post_process + template[field], + template.render_model, + tpl_res_ids, + engine=engine, + add_context=add_context, + options=options, + post_process=post_process ).items() ) diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py index 3939d56ae4c..920647e98c7 100644 --- a/addons/mail/models/mail_template.py +++ b/addons/mail/models/mail_template.py @@ -334,10 +334,16 @@ class MailTemplate(models.Model): # create a mail_mail based on values, without attachments values = self.generate_email( res_id, - ['subject', 'body_html', + ('auto_delete', + 'body_html', + 'email_cc', 'email_from', - 'email_cc', 'email_to', 'partner_to', 'reply_to', - 'auto_delete', 'scheduled_date'] + 'email_to', + 'partner_to', + 'reply_to', + 'scheduled_date', + 'subject', + ) ) values['recipient_ids'] = [Command.link(pid) for pid in values.get('partner_ids', list())] values['attachment_ids'] = [Command.link(aid) for aid in values.get('attachment_ids', list())] diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 1a8c7b837f5..df0afdd4cce 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -1977,9 +1977,11 @@ class MailThread(models.AbstractModel): if not msg_values.get('record_name'): msg_values['record_name'] = self.display_name msg_values.update({ + # author 'author_id': author_id, 'author_guest_id': author_guest_id, 'email_from': email_from, + # document 'model': self._name, 'res_id': self.id, # content @@ -2118,18 +2120,22 @@ class MailThread(models.AbstractModel): res_id = False msg_values = { - 'parent_id': parent_id, - 'model': self._name if self else model, - 'res_id': self.id if self else res_id, - 'message_type': 'user_notification', - 'subject': subject, - 'body': body, + # author 'author_id': author_id, 'email_from': email_from, - 'partner_ids': partner_ids, - 'is_internal': True, + # document + 'model': self._name if self else model, 'record_name': False, + 'res_id': self.id if self else res_id, + # content + 'body': body, + 'is_internal': True, + 'message_type': 'user_notification', + 'parent_id': parent_id, + 'subject': subject, + # recipients 'message_id': tools.generate_tracking_message_id('message-notify'), + 'partner_ids': partner_ids, } msg_values.update(msg_kwargs) # add default-like values afterwards, to avoid useless queries @@ -2159,19 +2165,23 @@ class MailThread(models.AbstractModel): author_id, email_from = self._message_compute_author(author_id, email_from, raise_on_email=False) msg_values = { - 'subject': subject, - 'body': body, + # author 'author_id': author_id, 'email_from': email_from, - 'message_type': message_type, + # document 'model': kwargs.get('model', self._name), - 'res_id': self.ids[0] if self.ids else False, - 'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'), - 'is_internal': True, 'record_name': False, - 'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from)[False], - 'message_id': tools.generate_tracking_message_id('message-notify'), # why? this is all but a notify + 'res_id': self.ids[0] if self.ids else False, + # content + 'body': body, + 'is_internal': True, + 'message_type': message_type, + 'subject': subject, + 'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'), + # recipients 'email_add_signature': False, # False as no notification -> no need to compute signature + 'message_id': tools.generate_tracking_message_id('message-notify'), # why? this is all but a notify + 'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from)[False], } msg_values.update(kwargs) @@ -2186,17 +2196,21 @@ class MailThread(models.AbstractModel): author_id, email_from = self._message_compute_author(author_id, email_from, raise_on_email=False) base_message_values = { - 'subject': subject, + # author 'author_id': author_id, 'email_from': email_from, - 'message_type': message_type, + # document 'model': self._name, - 'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'), - 'is_internal': True, 'record_name': False, - 'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from)[False], - 'message_id': tools.generate_tracking_message_id('message-notify'), # why? this is all but a notify + # content + 'message_type': message_type, + 'is_internal': True, + 'subject': subject, + 'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'), + # recipients 'email_add_signature': False, + 'message_id': tools.generate_tracking_message_id('message-notify'), # why? this is all but a notify + 'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from)[False], } values_list = [dict(base_message_values, res_id=record.id, @@ -2408,9 +2422,9 @@ class MailThread(models.AbstractModel): notif_create_values = [{ 'author_id': message.author_id.id, 'mail_message_id': message.id, - 'res_partner_id': pid, - 'notification_type': 'inbox', 'notification_status': 'sent', + 'notification_type': 'inbox', + 'res_partner_id': pid, } for pid in inbox_pids] self.env['mail.notification'].sudo().create(notif_create_values) @@ -2531,12 +2545,12 @@ class MailThread(models.AbstractModel): }) notif_create_values += [{ 'author_id': message.author_id.id, - 'mail_message_id': message.id, - 'res_partner_id': recipient_id, - 'notification_type': 'email', - 'mail_mail_id': new_email.id, 'is_read': True, # discard Inbox notification + 'mail_mail_id': new_email.id, + 'mail_message_id': message.id, 'notification_status': 'ready', + 'notification_type': 'email', + 'res_partner_id': recipient_id, } for recipient_id in tocreate_recipient_ids] emails += new_email diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py index d8061f99946..3912398d5c3 100644 --- a/addons/mail/tests/common.py +++ b/addons/mail/tests/common.py @@ -20,6 +20,7 @@ from odoo.addons.mail.models.mail_message import Message from odoo.addons.mail.models.mail_notification import MailNotification from odoo.tests import common, new_test_user from odoo.tools import formataddr, pycompat +from odoo.tools.translate import code_translations mail_new_test_user = partial(new_test_user, context={'mail_create_nolog': True, 'mail_create_nosubscribe': True, @@ -1119,9 +1120,16 @@ class MailCommon(common.TransactionCase, MailCase): if test_record: cls.env['ir.model']._get(test_record._name).with_context(lang=lang_code).name = 'Spanish description' + # Translate some code strings used in mailing + code_translations.python_translations[('test_mail', 'es_ES')] = {'TestStuff': 'TestSpanishStuff'} + cls.addClassCleanup(code_translations.python_translations.pop, ('test_mail', 'es_ES')) + code_translations.python_translations[('mail', 'es_ES')] = {'View %s': 'SpanishView %s'} + cls.addClassCleanup(code_translations.python_translations.pop, ('mail', 'es_ES')) + # Prepare some translated value for template if given - test_template.with_context(lang=lang_code).subject = 'SpanishSubject for {{ object.name }}' - test_template.with_context(lang=lang_code).body_html = '
SpanishBody for
SpanishBody for
{{ 13 + 13 }}
Bonjour
', @@ -136,6 +132,10 @@ class TestMailRender(common.MailCommon): ) cls.user_rendering_restricted.groups_id -= cls.env.ref('mail.group_mail_template_editor') + +@tagged('mail_render') +class TestMailRender(TestMailRenderCommon): + @users('employee') def test_evaluation_context(self): """ Test evaluation context and various ways of tweaking it. """ @@ -164,6 +164,15 @@ class TestMailRender(common.MailCommon): )[partner.id] self.assertEqual(expected, result) + @users('employee') + def test_prepend_preview_inline_template_to_qweb(self): + body = 'body' + preview = 'foo{{"false" if 1 > 2 else "true"}}bar' + result = self.env['mail.render.mixin']._prepend_preview(Markup(body), preview) + self.assertEqual(result, '''body''') + @users('employee') def test_render_field(self): template = self.env['mail.template'].browse(self.test_template.ids) @@ -176,6 +185,10 @@ class TestMailRender(common.MailCommon): )[partner.id] self.assertEqual(rendered, expected) + @users('employee') + def test_render_field_lang(self): + """ Test translation in french """ + template = self.env['mail.template'].browse(self.test_template.ids) partner = self.env['res.partner'].browse(self.render_object_fr.ids) for fname, expected in zip(['subject', 'body_html'], self.base_rendered_fr): rendered = template._render_field( @@ -197,23 +210,6 @@ class TestMailRender(common.MailCommon): )[partner.id] self.assertEqual(rendered, expected) - @users('employee') - def test_render_template_local_links(self): - local_links_template_bits = [ - '', - '', - '', - ] - base_url = self.env['mail.render.mixin'].get_base_url() - rendered_local_links = [ - '' % base_url, - '' % base_url, - '' % base_url - ] - for source, expected in zip(local_links_template_bits, rendered_local_links): - rendered = self.env['mail.render.mixin']._replace_local_links(source) - self.assertEqual(rendered, expected) - @users('employee') def test_render_template_qweb(self): partner = self.env['res.partner'].browse(self.render_object.ids) @@ -239,77 +235,7 @@ class TestMailRender(common.MailCommon): self.assertEqual(rendered, expected) @users('employee') - def test_template_rendering_impersonate(self): - """ Test that the use of SUDO do not change the current user. """ - partner = self.env['res.partner'].browse(self.render_object.ids) - src = '{{ user.name }} - {{ object.name }}' - expected = '%s - %s' % (self.env.user.name, partner.name) - result = self.env['mail.render.mixin'].sudo()._render_template_inline_template( - src, partner._name, partner.ids - )[partner.id] - self.assertIn(expected, result) - - @users('user_rendering_restricted') - def test_template_rendering_function_call(self): - """Test the case when the template call a custom function. - - This function should not be called when the template is not rendered. - """ - model = 'res.partner' - res_ids = self.env[model].search([], limit=1).ids - partner = self.env[model].browse(res_ids) - MailRenderMixin = self.env['mail.render.mixin'] - - def cust_function(): - # Can not use "MagicMock" in a Jinja sand-boxed environment - # so create our own function - cust_function.call = True - return 'return value' - - cust_function.call = False - - src = """{{ cust_function() }}
""" - expected = """return value
""" - context = {'cust_function': cust_function} - - result = self.env['mail.render.mixin'].with_user(self.user_admin)._render_template_inline_template( - src, partner._name, partner.ids, - add_context=context - )[partner.id] - self.assertEqual(expected, result) - self.assertTrue(cust_function.call) - - with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'): - MailRenderMixin._render_template_inline_template(src, model, res_ids, add_context=context) - - @users('user_rendering_restricted') - def test_template_render_static(self): - """Test that we render correctly static templates (without placeholders).""" - model = 'res.partner' - res_ids = self.env[model].search([], limit=1).ids - MailRenderMixin = self.env['mail.render.mixin'] - - result = MailRenderMixin._render_template_inline_template(self.base_inline_template_bits[0], model, res_ids)[res_ids[0]] - self.assertEqual(result, self.base_inline_template_bits[0]) - - @users('user_rendering_restricted') - def test_template_rendering_restricted(self): - """Test if we correctly detect static template.""" - res_ids = self.env['res.partner'].search([], limit=1).ids - with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'): - self.env['mail.render.mixin']._render_template_inline_template(self.base_inline_template_bits[3], 'res.partner', res_ids) - - @users('employee') - def test_template_rendering_unrestricted(self): - """Test if we correctly detect static template.""" - res_ids = self.env['res.partner'].search([], limit=1).ids - result = self.env['mail.render.mixin']._render_template_inline_template(self.base_inline_template_bits[3], 'res.partner', res_ids)[res_ids[0]] - self.assertIn('26', result, 'Template Editor should be able to render inline_template code') - - @users('employee') - def test_template_rendering_various(self): + def test_render_template_various(self): """ Test static rendering """ partner = self.env['res.partner'].browse(self.render_object.ids) MailRenderMixin = self.env['mail.render.mixin'] @@ -376,6 +302,131 @@ class TestMailRender(common.MailCommon): )[partner.id] self.assertEqual(result, expected) + @users('employee') + def test_replace_local_links(self): + local_links_template_bits = [ + '', + '', + '', + ] + base_url = self.env['mail.render.mixin'].get_base_url() + rendered_local_links = [ + '' % base_url, + '' % base_url, + '' % base_url + ] + for source, expected in zip(local_links_template_bits, rendered_local_links): + rendered = self.env['mail.render.mixin']._replace_local_links(source) + self.assertEqual(rendered, expected) + + +@tagged('mail_render') +class TestMailRenderSecurity(TestMailRenderCommon): + """ Test security of rendering, based on qweb finding + restricted rendering + group usage. """ + + @users('employee') + def test_render_inline_template_impersonate(self): + """ Test that the use of SUDO do not change the current user. """ + partner = self.env['res.partner'].browse(self.render_object.ids) + src = '{{ user.name }} - {{ object.name }}' + expected = '%s - %s' % (self.env.user.name, partner.name) + result = self.env['mail.render.mixin'].sudo()._render_template_inline_template( + src, partner._name, partner.ids + )[partner.id] + self.assertIn(expected, result) + + @users('user_rendering_restricted') + def test_render_inline_template_restricted(self): + """Test if we correctly detect static template.""" + res_ids = self.env['res.partner'].search([], limit=1).ids + with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'): + self.env['mail.render.mixin']._render_template_inline_template( + self.base_inline_template_bits[3], + 'res.partner', + res_ids + ) + + src = """{{ cust_function() }}
""" + expected = """return value
""" + context = {'cust_function': cust_function} + + result = self.env['mail.render.mixin'].with_user(self.user_admin)._render_template_inline_template( + src, partner._name, partner.ids, + add_context=context + )[partner.id] + self.assertEqual(expected, result) + self.assertTrue(cust_function.call) + + with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'): + MailRenderMixin._render_template_inline_template(src, model, res_ids, add_context=context) + @users('user_rendering_restricted') def test_security_inline_template_restricted(self): """Test if we correctly detect condition block (which might contains code).""" @@ -384,7 +435,7 @@ class TestMailRender(common.MailCommon): self.env['mail.render.mixin']._render_template_inline_template(self.base_inline_template_bits[4], 'res.partner', res_ids) @users('employee') - def test_is_inline_template_condition_block_unrestricted(self): + def test_security_inline_template_unrestricted(self): """Test if we correctly detect condition block (which might contains code).""" res_ids = self.env['res.partner'].search([], limit=1).ids result = self.env['mail.render.mixin']._render_template_inline_template(self.base_inline_template_bits[4], 'res.partner', res_ids)[res_ids[0]] @@ -417,36 +468,3 @@ class TestMailRender(common.MailCommon): res_ids = self.env['res.partner'].search([], limit=1).ids result = self.env['mail.render.mixin']._render_template_qweb(self.base_qweb_bits[1], 'res.partner', res_ids)[res_ids[0]] self.assertNotIn('Code not executed', result, 'The condition block did not work') - - @users('user_rendering_restricted') - def test_template_rendering_static_inline_template(self): - model = 'res.partner' - res_ids = self.env[model].search([], limit=1).ids - partner = self.env[model].browse(res_ids) - - src = """Hello TestingPartner
') + self.assertEqual(mail.body_html, 'Hello TestingPartner
') self.assertFalse(mail.is_notification) with self.mock_mail_gateway(mail_unlink_sent=True): mail.send() diff --git a/addons/test_mail/tests/test_mail_template.py b/addons/test_mail/tests/test_mail_template.py index 9be741b5fff..fb1bec1977e 100644 --- a/addons/test_mail/tests/test_mail_template.py +++ b/addons/test_mail/tests/test_mail_template.py @@ -64,9 +64,20 @@ class TestMailTemplateCommon(TestMailCommon, TestRecipients): cls.test_template.invalidate_recordset(['attachment_ids']) -@tagged('mail_template', 'multi_lang') +@tagged('mail_template') class TestMailTemplate(TestMailTemplateCommon): + def test_template_add_context_action(self): + self.test_template.create_action() + + # check template act_window has been updated + self.assertTrue(bool(self.test_template.ref_ir_act_window)) + + # check those records + action = self.test_template.ref_ir_act_window + self.assertEqual(action.name, 'Send Mail (%s)' % self.test_template.name) + self.assertEqual(action.binding_model_id.model, 'mail.test.lang') + @mute_logger('odoo.addons.mail.models.mail_mail') @users('employee') def test_template_schedule_email(self): @@ -93,6 +104,10 @@ class TestMailTemplate(TestMailTemplateCommon): self.assertFalse(mail.scheduled_date) self.assertEqual(mail.state, 'outgoing') + +@tagged('mail_template', 'multi_lang') +class TestMailTemplateLanguages(TestMailTemplateCommon): + @mute_logger('odoo.addons.mail.models.mail_mail') def test_template_send_email(self): mail_id = self.test_template.send_mail(self.test_record.id) @@ -134,54 +149,3 @@ class TestMailTemplate(TestMailTemplateCommon): self.assertEqual(mail.body_html, 'SpanishBody for %s
Spanish Layout para Spanish description' % self.test_record.name) self.assertEqual(mail.subject, 'SpanishSubject for %s' % self.test_record.name) - - def test_template_add_context_action(self): - self.test_template.create_action() - - # check template act_window has been updated - self.assertTrue(bool(self.test_template.ref_ir_act_window)) - - # check those records - action = self.test_template.ref_ir_act_window - self.assertEqual(action.name, 'Send Mail (%s)' % self.test_template.name) - self.assertEqual(action.binding_model_id.model, 'mail.test.lang') - - # def test_template_scheduled_date(self): - # from unittest.mock import patch - - # self.email_template_in_2_days = self.email_template.copy() - - # with patch('odoo.addons.mail.tests.test_mail_template.datetime', wraps=datetime) as mock_datetime: - # mock_datetime.now.return_value = datetime(2017, 11, 15, 11, 30, 28) - # mock_datetime.side_effect = lambda *args, **kw: datetime(*args, **kw) - - # self.email_template_in_2_days.write({ - # 'scheduled_date': "{{ (datetime.datetime.now() + relativedelta(days=2)).strftime('%s') }}" % DEFAULT_SERVER_DATETIME_FORMAT, - # }) - - # mail_now_id = self.email_template.send_mail(self.test_record.id) - # mail_in_2_days_id = self.email_template_in_2_days.send_mail(self.test_record.id) - - # mail_now = self.env['mail.mail'].browse(mail_now_id) - # mail_in_2_days = self.env['mail.mail'].browse(mail_in_2_days_id) - - # # mail preparation - # self.assertEqual(mail_now.exists() | mail_in_2_days.exists(), mail_now | mail_in_2_days) - # self.assertEqual(bool(mail_now.scheduled_date), False) - # self.assertEqual(mail_now.state, 'outgoing') - # self.assertEqual(mail_in_2_days.state, 'outgoing') - # scheduled_date = datetime.strptime(mail_in_2_days.scheduled_date, DEFAULT_SERVER_DATETIME_FORMAT) - # date_in_2_days = datetime.now() + timedelta(days = 2) - # self.assertEqual(scheduled_date, date_in_2_days) - # # self.assertEqual(scheduled_date.month, date_in_2_days.month) - # # self.assertEqual(scheduled_date.year, date_in_2_days.year) - - # # Launch the scheduler on the first mail, it should be reported in self.mails - # # and the mail_mail is now deleted - # self.env['mail.mail'].process_email_queue() - # self.assertEqual(mail_now.exists() | mail_in_2_days.exists(), mail_in_2_days) - - # # Launch the scheduler on the first mail, it's still in 'outgoing' state - # self.env['mail.mail'].process_email_queue(ids=[mail_in_2_days.id]) - # self.assertEqual(mail_in_2_days.state, 'outgoing') - # self.assertEqual(mail_now.exists() | mail_in_2_days.exists(), mail_in_2_days) diff --git a/addons/test_mail/tests/test_message_post.py b/addons/test_mail/tests/test_message_post.py index 56ddcbf4a2b..7341c9fd44a 100644 --- a/addons/test_mail/tests/test_message_post.py +++ b/addons/test_mail/tests/test_message_post.py @@ -19,7 +19,6 @@ from odoo.exceptions import AccessError from odoo.tests import tagged from odoo.tools import mute_logger, formataddr from odoo.tests.common import users -from odoo.tools.translate import code_translations class TestMessagePostCommon(TestMailCommon, TestRecipients): @@ -1241,12 +1240,6 @@ class TestMessagePostLang(TestMailCommon, TestRecipients): @users('employee') @mute_logger('odoo.addons.mail.models.mail_mail') def test_layout_email_lang_context(self): - # hack into code translations: - code_translations.python_translations[('test_mail', 'es_ES')] = {'TestStuff': 'TestSpanishStuff'} - self.addCleanup(code_translations.python_translations.pop, ('test_mail', 'es_ES')) - code_translations.python_translations[('mail', 'es_ES')] = {'View %s': 'SpanishView %s'} - self.addCleanup(code_translations.python_translations.pop, ('mail', 'es_ES')) - test_records = self.test_records.with_user(self.env.user).with_context(lang='es_ES') test_records[1].message_subscribe(self.partner_2.ids) diff --git a/addons/test_mass_mailing/tests/test_mailing_test.py b/addons/test_mass_mailing/tests/test_mailing_test.py index d3bca1cf3c7..5e0d8ac149a 100644 --- a/addons/test_mass_mailing/tests/test_mailing_test.py +++ b/addons/test_mass_mailing/tests/test_mailing_test.py @@ -4,10 +4,11 @@ import lxml.html from odoo.addons.test_mass_mailing.tests.common import TestMassMailCommon from odoo.fields import Command -from odoo.tests.common import users +from odoo.tests.common import users, tagged from odoo.tools import mute_logger +@tagged('mailing_manage') class TestMailingTest(TestMassMailCommon): @users('user_marketing') diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index 26151b6f7b7..8654008772e 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -134,7 +134,16 @@ class ChannelUsersRelation(models.Model): record_email_values = dict() for template, records in template_to_records.items(): - record_email_values.update(template.generate_email(records.ids, ['subject', 'body_html', 'email_from', 'partner_to'])) + record_email_values.update( + template.generate_email( + records.ids, + ['body_html', + 'email_from', + 'partner_to', + 'subject', + ] + ) + ) mail_mail_values = [] for record in self: