[LINT] mail, various: perform a quick code linting
Purpose is to try to have code easier to read and to update by having a
common way of sorting / writing code. Reorder some fields definitions and
dictionaries, update docstrings, ... in mail applications or when invoking
mail thread API.
Additional stuff worth noting here
* add some tagged in tests helping debugging / choosing tests to execute;
* in a test about mail generation with server action: correctly check
body_html field, not body which comes from the mail.message inheritance
(currently filled due to a side effect but actual field to check is the
html one);
* add same check on input for ``_render_template_qweb`` as done on other
rendering methods (even if rendering on [False] should be supported);
* move code translation update from specific tests into main test class
(code update due to new translations, followup of odoo/odoo@f8c2b02abe);
Some test linting done here
* reorder mail_render tests, remove some duplicated tests;
* reorder mail_template tests, remove some duplicated tests;
Task-2710804 (Mail: Clean MailThread Posting API)
closes odoo/odoo#106025
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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())]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 = '<p>SpanishBody for <t t-out="object.name" /></p>'
|
||||
if test_template:
|
||||
test_template.with_context(lang=lang_code).subject = 'SpanishSubject for {{ object.name }}'
|
||||
test_template.with_context(lang=lang_code).body_html = '<p>SpanishBody for <t t-out="object.name" /></p>'
|
||||
|
||||
# create a custom layout for email notification
|
||||
if not layout_arch_db:
|
||||
|
||||
@@ -8,12 +8,11 @@ from odoo.exceptions import AccessError
|
||||
from odoo.tests import tagged, users
|
||||
|
||||
|
||||
@tagged('mail_render')
|
||||
class TestMailRender(common.MailCommon):
|
||||
class TestMailRenderCommon(common.MailCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestMailRender, cls).setUpClass()
|
||||
super(TestMailRenderCommon, cls).setUpClass()
|
||||
|
||||
# activate multi language support
|
||||
cls.env['res.lang']._activate_lang('fr_FR')
|
||||
@@ -40,10 +39,7 @@ class TestMailRender(common.MailCommon):
|
||||
<p>{{ 13 + 13 }}</p>
|
||||
<h1>This is a test</h1>
|
||||
""",
|
||||
"""
|
||||
<b>Test</b>
|
||||
{{ '' if True else '<b>Code not executed</b>' }}
|
||||
""",
|
||||
"""<b>Test</b>{{ '' if True else '<b>Code not executed</b>' }}""",
|
||||
]
|
||||
cls.base_inline_template_bits_fr = [
|
||||
'<p>Bonjour</p>',
|
||||
@@ -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, '''<div style="display:none;font-size:1px;height:0px;width:0px;opacity:0;">
|
||||
foo<t t-out=""false" if 1 > 2 else "true""/>bar
|
||||
</div>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 = [
|
||||
'<div style="background-image:url(/web/path?a=a&b=b);"/>',
|
||||
'<div style="background-image:url(\'/web/path?a=a&b=b\');"/>',
|
||||
'<div style="background-image:url("/web/path?a=a&b=b");"/>',
|
||||
]
|
||||
base_url = self.env['mail.render.mixin'].get_base_url()
|
||||
rendered_local_links = [
|
||||
'<div style="background-image:url(%s/web/path?a=a&b=b);"/>' % base_url,
|
||||
'<div style="background-image:url(\'%s/web/path?a=a&b=b\');"/>' % base_url,
|
||||
'<div style="background-image:url("%s/web/path?a=a&b=b");"/>' % 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 = """<h1>This is a test</h1>
|
||||
<p>{{ cust_function() }}</p>"""
|
||||
expected = """<h1>This is a test</h1>
|
||||
<p>return value</p>"""
|
||||
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 = [
|
||||
'<div style="background-image:url(/web/path?a=a&b=b);"/>',
|
||||
'<div style="background-image:url(\'/web/path?a=a&b=b\');"/>',
|
||||
'<div style="background-image:url("/web/path?a=a&b=b");"/>',
|
||||
]
|
||||
base_url = self.env['mail.render.mixin'].get_base_url()
|
||||
rendered_local_links = [
|
||||
'<div style="background-image:url(%s/web/path?a=a&b=b);"/>' % base_url,
|
||||
'<div style="background-image:url(\'%s/web/path?a=a&b=b\');"/>' % base_url,
|
||||
'<div style="background-image:url("%s/web/path?a=a&b=b");"/>' % 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 = """<h1>This is a static template</h1>"""
|
||||
result = self.env['mail.render.mixin']._render_template_inline_template(
|
||||
src,
|
||||
'res.partner',
|
||||
res_ids
|
||||
)[res_ids[0]]
|
||||
self.assertEqual(src, str(result))
|
||||
|
||||
@users('user_rendering_restricted')
|
||||
def test_render_inline_template_restricted_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('employee')
|
||||
def test_render_inline_template_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('user_rendering_restricted')
|
||||
def test_render_template_qweb_restricted(self):
|
||||
model = 'res.partner'
|
||||
res_ids = self.env[model].search([], limit=1).ids
|
||||
partner = self.env[model].browse(res_ids)
|
||||
|
||||
src = """<h1>This is a static template</h1>"""
|
||||
|
||||
result = self.env['mail.render.mixin']._render_template_qweb(src, model, res_ids)[
|
||||
partner.id]
|
||||
self.assertEqual(src, str(result))
|
||||
|
||||
@users('user_rendering_restricted')
|
||||
def test_security_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 = """<h1>This is a test</h1>
|
||||
<p>{{ cust_function() }}</p>"""
|
||||
expected = """<h1>This is a test</h1>
|
||||
<p>return value</p>"""
|
||||
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 = """<h1>This is a static template</h1>"""
|
||||
|
||||
result = self.env['mail.render.mixin']._render_template_inline_template(
|
||||
src, model, res_ids)[partner.id]
|
||||
self.assertEqual(src, str(result))
|
||||
|
||||
@users('user_rendering_restricted')
|
||||
def test_template_rendering_static_qweb(self):
|
||||
model = 'res.partner'
|
||||
res_ids = self.env[model].search([], limit=1).ids
|
||||
partner = self.env[model].browse(res_ids)
|
||||
|
||||
src = """<h1>This is a static template</h1>"""
|
||||
|
||||
result = self.env['mail.render.mixin']._render_template_qweb(src, model, res_ids)[
|
||||
partner.id]
|
||||
self.assertEqual(src, str(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, '''<div style="display:none;font-size:1px;height:0px;width:0px;opacity:0;">
|
||||
foo<t t-out=""false" if 1 > 2 else "true""/>bar
|
||||
</div>body''')
|
||||
|
||||
@@ -5,10 +5,11 @@ from markupsafe import Markup
|
||||
from odoo.addons.mail.tests.common import MailCommon
|
||||
from odoo.exceptions import AccessError, UserError
|
||||
from odoo.modules.module import get_module_resource
|
||||
from odoo.tests import Form, users
|
||||
from odoo.tests import Form, tagged, users
|
||||
from odoo.tools import convert_file
|
||||
|
||||
|
||||
@tagged('mail_template')
|
||||
class TestMailTemplate(MailCommon):
|
||||
|
||||
@classmethod
|
||||
@@ -147,6 +148,7 @@ class TestMailTemplate(MailCommon):
|
||||
self.assertFalse(server.active)
|
||||
|
||||
|
||||
@tagged('mail_template')
|
||||
class TestMailTemplateReset(MailCommon):
|
||||
|
||||
def _load(self, module, *args):
|
||||
|
||||
@@ -387,18 +387,18 @@ class MailComposer(models.TransientModel):
|
||||
for res_id in res_ids:
|
||||
# static wizard (mail.message) values
|
||||
mail_values = {
|
||||
'subject': self.subject,
|
||||
'body': self.body or '',
|
||||
'parent_id': self.parent_id and self.parent_id.id,
|
||||
'partner_ids': [partner.id for partner in self.partner_ids],
|
||||
'attachment_ids': [attach.id for attach in self.attachment_ids],
|
||||
'author_id': self.author_id.id,
|
||||
'body': self.body or '',
|
||||
'email_from': self.email_from,
|
||||
'record_name': self.record_name,
|
||||
'reply_to_force_new': self.reply_to_force_new,
|
||||
'mail_server_id': self.mail_server_id.id,
|
||||
'mail_activity_type_id': self.mail_activity_type_id.id,
|
||||
'message_type': 'email' if mass_mail_mode else self.message_type,
|
||||
'parent_id': self.parent_id.id,
|
||||
'partner_ids': self.partner_ids.ids,
|
||||
'record_name': self.record_name,
|
||||
'reply_to_force_new': self.reply_to_force_new,
|
||||
'subject': self.subject,
|
||||
}
|
||||
|
||||
# mass mailing: rendering override wizard static values
|
||||
@@ -554,10 +554,12 @@ class MailComposer(models.TransientModel):
|
||||
template = self.env['mail.template'].browse(template_id)
|
||||
values = dict(
|
||||
(field, template[field])
|
||||
for field in ['subject', 'body_html',
|
||||
for field in ('body_html',
|
||||
'email_from',
|
||||
'mail_server_id',
|
||||
'reply_to',
|
||||
'mail_server_id']
|
||||
'subject',
|
||||
)
|
||||
if template[field]
|
||||
)
|
||||
if template.attachment_ids:
|
||||
@@ -567,11 +569,16 @@ class MailComposer(models.TransientModel):
|
||||
elif template_id:
|
||||
values = self.generate_email_for_composer(
|
||||
template_id, [res_id],
|
||||
['subject', 'body_html',
|
||||
('attachment_ids',
|
||||
'body_html',
|
||||
'email_cc',
|
||||
'email_from',
|
||||
'email_cc', 'email_to', 'partner_to', 'reply_to',
|
||||
'attachment_ids', 'mail_server_id'
|
||||
]
|
||||
'email_to',
|
||||
'mail_server_id',
|
||||
'partner_to',
|
||||
'reply_to',
|
||||
'subject',
|
||||
)
|
||||
)[res_id]
|
||||
# transform attachments into attachment_ids; not attached to the document because this will
|
||||
# be done further in the posting process, allowing to clean database if email not send
|
||||
@@ -593,19 +600,28 @@ class MailComposer(models.TransientModel):
|
||||
default_composition_mode=composition_mode,
|
||||
default_model=model,
|
||||
default_res_id=res_id
|
||||
).default_get(['composition_mode', 'model', 'res_id', 'parent_id',
|
||||
'subject', 'body',
|
||||
).default_get(['attachment_ids',
|
||||
'body',
|
||||
'composition_mode',
|
||||
'email_from',
|
||||
'partner_ids', 'reply_to',
|
||||
'attachment_ids', 'mail_server_id'
|
||||
'mail_server_id',
|
||||
'model',
|
||||
'parent_id',
|
||||
'partner_ids',
|
||||
'reply_to',
|
||||
'res_id',
|
||||
'subject',
|
||||
])
|
||||
values = dict(
|
||||
(key, default_values[key])
|
||||
for key in ['subject', 'body',
|
||||
for key in ('attachment_ids',
|
||||
'body',
|
||||
'email_from',
|
||||
'partner_ids', 'reply_to',
|
||||
'attachment_ids', 'mail_server_id'
|
||||
] if key in default_values)
|
||||
'mail_server_id',
|
||||
'partner_ids',
|
||||
'reply_to',
|
||||
'subject',
|
||||
) if key in default_values)
|
||||
|
||||
if values.get('body_html'):
|
||||
values['body'] = values.pop('body_html')
|
||||
@@ -653,10 +669,10 @@ class MailComposer(models.TransientModel):
|
||||
results = dict.fromkeys(res_ids, False)
|
||||
for res_id in res_ids:
|
||||
results[res_id] = {
|
||||
'subject': subjects[res_id],
|
||||
'body': bodies[res_id],
|
||||
'email_from': emails_from[res_id],
|
||||
'reply_to': replies_to[res_id],
|
||||
'subject': subjects[res_id],
|
||||
}
|
||||
results[res_id].update(default_recipients.get(res_id, dict()))
|
||||
|
||||
@@ -664,7 +680,13 @@ class MailComposer(models.TransientModel):
|
||||
if self.template_id:
|
||||
template_values = self.generate_email_for_composer(
|
||||
self.template_id.id, res_ids,
|
||||
['email_to', 'partner_to', 'email_cc', 'attachment_ids', 'mail_server_id'])
|
||||
('attachment_ids',
|
||||
'email_to',
|
||||
'email_cc',
|
||||
'mail_server_id',
|
||||
'partner_to',
|
||||
)
|
||||
)
|
||||
else:
|
||||
template_values = {}
|
||||
|
||||
@@ -692,7 +714,7 @@ class MailComposer(models.TransientModel):
|
||||
multi_mode = False
|
||||
res_ids = [res_ids]
|
||||
|
||||
returned_fields = fields + ['partner_ids', 'attachments']
|
||||
returned_fields = list(fields) + ['partner_ids', 'attachments']
|
||||
values = dict.fromkeys(res_ids, False)
|
||||
|
||||
template_values = self.env['mail.template'].with_context(tpl_partners_only=True).browse(template_id).generate_email(res_ids, fields)
|
||||
|
||||
@@ -8,8 +8,15 @@ from odoo.exceptions import UserError
|
||||
class MailTemplatePreview(models.TransientModel):
|
||||
_name = 'mail.template.preview'
|
||||
_description = 'Email Template Preview'
|
||||
_MAIL_TEMPLATE_FIELDS = ['subject', 'body_html', 'email_from', 'email_to',
|
||||
'email_cc', 'reply_to', 'scheduled_date', 'attachment_ids']
|
||||
_MAIL_TEMPLATE_FIELDS = ['attachment_ids',
|
||||
'body_html',
|
||||
'subject',
|
||||
'email_cc',
|
||||
'email_from',
|
||||
'email_to',
|
||||
'reply_to',
|
||||
'scheduled_date',
|
||||
]
|
||||
|
||||
@api.model
|
||||
def _selection_target_model(self):
|
||||
@@ -70,7 +77,9 @@ class MailTemplatePreview(models.TransientModel):
|
||||
else:
|
||||
copy_depends_values['resource_ref'] = '%s,%s' % (self.resource_ref._name, self.resource_ref.id)
|
||||
mail_values = mail_template.with_context(template_preview_lang=self.lang).generate_email(
|
||||
self.resource_ref.id, self._MAIL_TEMPLATE_FIELDS)
|
||||
self.resource_ref.id,
|
||||
self._MAIL_TEMPLATE_FIELDS
|
||||
)
|
||||
self._set_mail_attributes(values=mail_values)
|
||||
self.error_msg = False
|
||||
except UserError as user_error:
|
||||
|
||||
@@ -1047,16 +1047,16 @@ class MassMailing(models.Model):
|
||||
'author_id': author_id,
|
||||
'attachment_ids': [(4, attachment.id) for attachment in mailing.attachment_ids],
|
||||
'body': mailing._prepend_preview(mailing.body_html, mailing.preview),
|
||||
'subject': mailing.subject,
|
||||
'model': mailing.mailing_model_real,
|
||||
'email_from': mailing.email_from,
|
||||
'record_name': False,
|
||||
'composition_mode': 'mass_mail',
|
||||
'mass_mailing_id': mailing.id,
|
||||
'mailing_list_ids': [(4, l.id) for l in mailing.contact_list_ids],
|
||||
'reply_to_force_new': mailing.reply_to_mode == 'new',
|
||||
'template_id': None,
|
||||
'email_from': mailing.email_from,
|
||||
'mail_server_id': mailing.mail_server_id.id,
|
||||
'mailing_list_ids': [(4, l.id) for l in mailing.contact_list_ids],
|
||||
'mass_mailing_id': mailing.id,
|
||||
'model': mailing.mailing_model_real,
|
||||
'record_name': False,
|
||||
'reply_to_force_new': mailing.reply_to_mode == 'new',
|
||||
'subject': mailing.subject,
|
||||
'template_id': None,
|
||||
}
|
||||
if mailing.reply_to_mode == 'new':
|
||||
composer_values['reply_to'] = mailing.reply_to
|
||||
|
||||
@@ -23,7 +23,6 @@ class MailComposeMessage(models.TransientModel):
|
||||
""" Override method that generated the mail content by creating the
|
||||
mailing.trace values in the o2m of mail_mail, when doing pure
|
||||
email mass mailing. """
|
||||
now = fields.Datetime.now()
|
||||
self.ensure_one()
|
||||
res = super(MailComposeMessage, self).get_mail_values(res_ids)
|
||||
# use only for allowed models in mass mailing
|
||||
@@ -32,37 +31,30 @@ class MailComposeMessage(models.TransientModel):
|
||||
self.model_is_thread:
|
||||
mass_mailing = self.mass_mailing_id
|
||||
if not mass_mailing:
|
||||
mass_mailing = self.env['mailing.mailing'].create({
|
||||
'campaign_id': self.campaign_id.id,
|
||||
'name': self.mass_mailing_name,
|
||||
'subject': self.subject,
|
||||
'state': 'done',
|
||||
'reply_to_mode': self.reply_to_mode,
|
||||
'reply_to': self.reply_to if self.reply_to_mode == 'new' else False,
|
||||
'sent_date': now,
|
||||
'body_html': self.body,
|
||||
'mailing_model_id': self.env['ir.model']._get(self.model).id,
|
||||
'mailing_domain': self.active_domain,
|
||||
'attachment_ids': [(6, 0, self.attachment_ids.ids)],
|
||||
})
|
||||
mass_mailing = self.env['mailing.mailing'].create(
|
||||
self._prepare_mailing_values()
|
||||
)
|
||||
self.mass_mailing_id = mass_mailing.id
|
||||
|
||||
recipients_info = self._process_recipient_values(res)
|
||||
for res_id in res_ids:
|
||||
mail_values = res[res_id]
|
||||
if mail_values.get('body_html'):
|
||||
body = self.env['ir.qweb']._render('mass_mailing.mass_mailing_mail_layout',
|
||||
{'body': mail_values['body_html']},
|
||||
minimal_qcontext=True, raise_if_not_found=False)
|
||||
body = self.env['ir.qweb']._render(
|
||||
'mass_mailing.mass_mailing_mail_layout',
|
||||
{'body': mail_values['body_html']},
|
||||
minimal_qcontext=True,
|
||||
raise_if_not_found=False
|
||||
)
|
||||
if body:
|
||||
mail_values['body_html'] = body
|
||||
|
||||
trace_vals = {
|
||||
'model': self.model,
|
||||
'res_id': res_id,
|
||||
'mass_mailing_id': mass_mailing.id,
|
||||
# if mail_to is void, keep falsy values to allow searching / debugging traces
|
||||
'email': recipients_info[res_id]['mail_to'][0] if recipients_info[res_id]['mail_to'] else '',
|
||||
'mass_mailing_id': mass_mailing.id,
|
||||
'model': self.model,
|
||||
'res_id': res_id,
|
||||
}
|
||||
# propagate failed states to trace when still-born
|
||||
if mail_values.get('state') == 'cancel':
|
||||
@@ -73,11 +65,11 @@ class MailComposeMessage(models.TransientModel):
|
||||
trace_vals['failure_type'] = mail_values['failure_type']
|
||||
|
||||
mail_values.update({
|
||||
'mailing_id': mass_mailing.id,
|
||||
'mailing_trace_ids': [(0, 0, trace_vals)],
|
||||
'auto_delete': not mass_mailing.keep_archives,
|
||||
# email-mode: keep original message for routing
|
||||
'is_notification': mass_mailing.reply_to_mode == 'update',
|
||||
'auto_delete': not mass_mailing.keep_archives,
|
||||
'mailing_id': mass_mailing.id,
|
||||
'mailing_trace_ids': [(0, 0, trace_vals)],
|
||||
})
|
||||
return res
|
||||
|
||||
@@ -92,3 +84,19 @@ class MailComposeMessage(models.TransientModel):
|
||||
if self.mass_mailing_id:
|
||||
opt_out_list += self.mass_mailing_id._get_opt_out_list()
|
||||
return opt_out_list
|
||||
|
||||
def _prepare_mailing_values(self):
|
||||
now = fields.Datetime.now()
|
||||
return {
|
||||
'attachment_ids': [(6, 0, self.attachment_ids.ids)],
|
||||
'body_html': self.body,
|
||||
'campaign_id': self.campaign_id.id,
|
||||
'mailing_model_id': self.env['ir.model']._get(self.model).id,
|
||||
'mailing_domain': self.active_domain,
|
||||
'name': self.mass_mailing_name,
|
||||
'reply_to': self.reply_to if self.reply_to_mode == 'new' else False,
|
||||
'reply_to_mode': self.reply_to_mode,
|
||||
'sent_date': now,
|
||||
'state': 'done',
|
||||
'subject': self.subject,
|
||||
}
|
||||
|
||||
@@ -84,12 +84,12 @@ class SaleOrderCancel(models.TransientModel):
|
||||
def action_send_mail_and_cancel(self):
|
||||
self.ensure_one()
|
||||
self.order_id.message_post(
|
||||
subject=self.subject,
|
||||
body=self.body,
|
||||
message_type='comment',
|
||||
email_from=self.email_from,
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
partner_ids=self.recipient_ids.ids,
|
||||
subject=self.subject,
|
||||
)
|
||||
return self.action_cancel()
|
||||
|
||||
|
||||
@@ -213,14 +213,14 @@ class SurveyInvite(models.TransientModel):
|
||||
body = self._render_field('body', answer.ids, post_process=True)[answer.id]
|
||||
# post the message
|
||||
mail_values = {
|
||||
'email_from': self.email_from,
|
||||
'attachment_ids': [(4, att.id) for att in self.attachment_ids],
|
||||
'auto_delete': True,
|
||||
'author_id': self.author_id.id,
|
||||
'body_html': body,
|
||||
'email_from': self.email_from,
|
||||
'model': None,
|
||||
'res_id': None,
|
||||
'subject': subject,
|
||||
'body_html': body,
|
||||
'attachment_ids': [(4, att.id) for att in self.attachment_ids],
|
||||
'auto_delete': True,
|
||||
}
|
||||
if answer.partner_id:
|
||||
mail_values['recipient_ids'] = [(4, answer.partner_id.id)]
|
||||
|
||||
@@ -41,7 +41,7 @@ class TestServerActionsEmail(TestMailCommon, TestServerActionsBase):
|
||||
mail = self.env['mail.mail'].sudo().search([('subject', '=', 'About TestingPartner')])
|
||||
self.assertEqual(len(mail), 1)
|
||||
self.assertTrue(mail.auto_delete)
|
||||
self.assertEqual(mail.body, '<p>Hello TestingPartner</p>')
|
||||
self.assertEqual(mail.body_html, '<p>Hello TestingPartner</p>')
|
||||
self.assertFalse(mail.is_notification)
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
mail.send()
|
||||
|
||||
@@ -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,
|
||||
'<body><p>SpanishBody for %s</p> Spanish Layout para Spanish description</body>' % 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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user