diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 4c790e86607..e69e4491f8c 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -3012,17 +3012,24 @@ class MailThread(models.AbstractModel): if not partners_data: return True - model = msg_vals.get('model') if msg_vals else message.model - model_name = model_description or (self.env['ir.model']._get(model).display_name if model else False) # one query for display name - recipients_groups_list = self._notify_get_recipients_classify( - message, partners_data, model_name, msg_vals=msg_vals + lang = force_email_lang if force_email_lang else self.env.context.get('lang') + + if not model_description: + model_description = self.with_context(lang=lang)._get_model_description( + msg_vals['model'] if msg_vals and msg_vals.get('model') else message.model + ) + recipients_groups_list = self.with_context(lang=lang)._notify_get_recipients_classify( + message, + partners_data, + model_description, + msg_vals=msg_vals, ) if not recipients_groups_list: return True force_send = self.env.context.get('mail_notify_force_send', force_send) - template_values = self._notify_by_email_prepare_rendering_context( + template_values = self.with_context(lang=lang)._notify_by_email_prepare_rendering_context( message, msg_vals=msg_vals, model_description=model_description, force_email_company=force_email_company, force_email_lang=force_email_lang, @@ -3032,7 +3039,10 @@ class MailThread(models.AbstractModel): email_layout_xmlid = msg_vals.get('email_layout_xmlid') if msg_vals else message.email_layout_xmlid template_xmlid = email_layout_xmlid if email_layout_xmlid else 'mail.mail_notification_layout' - base_mail_values = self._notify_by_email_get_base_mail_values(message, additional_values={'auto_delete': mail_auto_delete}) + base_mail_values = self.with_context(lang=lang)._notify_by_email_get_base_mail_values( + message, + additional_values={'auto_delete': mail_auto_delete}, + ) # Clean the context to get rid of residual default_* keys that could cause issues during # the mail.mail creation. @@ -3158,6 +3168,8 @@ class MailThread(models.AbstractModel): """ if msg_vals is False: msg_vals = {} + lang = force_email_lang if force_email_lang else self.env.context.get('lang') + record_wlang = self.with_context(lang=lang) # compute send user and its related signature; try to use self.env.user instead of browsing # user_ids if they are the author will give a sudo user, improving access performances and cache usage. @@ -3174,33 +3186,19 @@ class MailThread(models.AbstractModel): if force_email_company: company = force_email_company else: - company = self.company_id.sudo() if self and 'company_id' in self and self.company_id else self.env.company + company = record_wlang.company_id.sudo() if ( + record_wlang and 'company_id' in record_wlang and record_wlang.company_id + ) else record_wlang.env.company if company.website: website_url = 'http://%s' % company.website if not company.website.lower().startswith(('http:', 'https:')) else company.website else: website_url = False - # compute lang in which content was rendered or typed - lang = False - if force_email_lang: - lang = force_email_lang - elif {'default_template_id', 'default_model', 'default_res_ids'} <= self.env.context.keys(): - # TDE FIXME: this whole brol should be cleaned ! - if msg_vals.get('res_id'): - res_ids = [msg_vals['res_id']] - else: - res_ids = self.env['mail.compose.message']._parse_res_ids(self.env.context['default_res_ids']) - template = self.env['mail.template'].browse(self.env.context['default_template_id']) - if res_ids and template and template.lang: - lang = template._render_lang(res_ids)[res_ids[0]] - if not lang: - lang = self.env.context.get('lang') - # record, model if not model_description: - model = msg_vals.get('model') if 'model' in msg_vals else message.model - if model: - model_description = self.env['ir.model'].with_context(lang=lang)._get(model).display_name + model_description = record_wlang._get_model_description( + msg_vals.get('model') if 'model' in msg_vals else message.model + ) record_name = msg_vals.get('record_name') if 'record_name' in msg_vals else message.record_name # tracking @@ -3224,7 +3222,7 @@ class MailThread(models.AbstractModel): 'tracking_values': tracking, # record 'model_description': model_description, - 'record': self, + 'record': record_wlang, 'record_name': record_name, 'subtitles': [record_name], # user / environment @@ -3605,6 +3603,14 @@ class MailThread(models.AbstractModel): hm = hmac.new(secret.encode('utf-8'), token.encode('utf-8'), hashlib.sha1).hexdigest() return hm + @api.model + def _get_model_description(self, model_name): + if not model_name: + return False + if not 'lang' in self.env.context: + raise ValueError(_('At this point lang should be correctly set')) + return self.env['ir.model']._get(model_name).display_name # one query for display name + def _is_thread_message(self, msg_vals=None): """ Tool method to compute thread validity in notification methods. msg_vals is used as a replacement for self, allowing to force model diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index e2a8141acf2..1b5229cc73c 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -549,6 +549,19 @@ class MailComposer(models.TransientModel): if not composer.template_id: composer.scheduled_date = False + # Overrides of mail.compose.mixin + @api.depends('template_id') + def _compute_lang(self): + """ Computation is coming either from template, either reset. When + having a template with a value set, copy it (in batch mode) or render + it (in monorecord comment mode) on the composer. When removing the + template, reset it. """ + for composer in self: + if composer.template_id: + composer._set_value_from_template('lang') + if not composer.template_id: + composer.lang = False + # Overrides of mail.render.mixin @api.depends('model') def _compute_render_model(self): @@ -743,6 +756,7 @@ class MailComposer(models.TransientModel): DYN - 'body', STA - 'email_add_signature', STA - 'email_layout_xmlid', + DYN - 'force_email_lang', # notify parameter BOTH DYN - 'attachment_ids', @@ -861,8 +875,10 @@ class MailComposer(models.TransientModel): RecordsModel = self.env[self.model].with_prefetch(res_ids) email_mode = self.composition_mode == 'mass_mail' + # langs, used currently only to propagate in comment mode for notification + # layout translation + langs = self._render_field('lang', res_ids) if not email_mode else {} subjects = self._render_field('subject', res_ids, compute_lang=True) - # We want to preserve comments in emails so as to keep mso conditionals bodies = self._render_field( 'body', res_ids, compute_lang=True, # We want to preserve comments in emails so as to keep mso conditionals @@ -881,7 +897,14 @@ class MailComposer(models.TransientModel): { 'body_html': bodies[res_id], 'res_id': res_id, - } if email_mode else {}), + } if email_mode else {} + ), + **( + { + # notify parameter to force layout lang + 'force_email_lang': langs[res_id], + } if not email_mode else {} + ), } for res_id in res_ids } @@ -979,6 +1002,7 @@ class MailComposer(models.TransientModel): populate in '_prepare_mail_values'; """ self.ensure_one() + email_mode = self.composition_mode == 'mass_mail' # Duplicate attachments linked to the email.template. Indeed, composer # duplicates attachments in mass mode. But in 'rendered' mode attachments @@ -1005,6 +1029,12 @@ class MailComposer(models.TransientModel): 'partner_ids': self.partner_ids.ids, 'scheduled_date': self.scheduled_date, 'subject': self.subject or '', + **( + { + # notify parameter to force layout lang + 'force_email_lang': self.lang, + } if not email_mode else {} + ), } for res_id in res_ids } diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py index c9ab79141fc..4c3f696ef50 100644 --- a/addons/test_mail/tests/test_mail_composer.py +++ b/addons/test_mail/tests/test_mail_composer.py @@ -1581,25 +1581,20 @@ class TestComposerResultsComment(TestMailComposer, CronMixinCase): email = self._find_sent_email(test_record.user_id.email_formatted, [test_record.customer_id.email_formatted]) self.assertTrue(bool(email), 'Email not found, check recipients') - # TDE FIXME: as it currently depends on a context-based hack - # translation is not supported when scheduling notifications - # or when a domain is given. Moreover access buttons are not - # translated exp_layout_content_en = 'English Layout for Ticket-like model' exp_layout_content_es = 'Spanish Layout para Spanish Model Description' exp_button_en = 'View Ticket-like model' - # exp_button_es = 'SpanishView Spanish Model Description' + exp_button_es = 'SpanishView Spanish Model Description' if email_layout_xmlid: - if exp_lang == 'es_ES' and batch_mode != 'domain' and not scheduled_date: + if exp_lang == 'es_ES': self.assertIn(exp_layout_content_es, email['body']) - self.assertIn(exp_button_en, email['body'], - 'TODO: buttons should be translated') + self.assertIn(exp_button_es, email['body']) else: self.assertIn(exp_layout_content_en, email['body']) self.assertIn(exp_button_en, email['body']) else: # check default layouting applies - if exp_lang == 'es_ES' and batch_mode != 'domain' and not scheduled_date: + if exp_lang == 'es_ES': self.assertIn('html lang="es_ES"', email['body']) else: self.assertIn('html lang="en_US"', email['body']) diff --git a/addons/test_mail/tests/test_message_post.py b/addons/test_mail/tests/test_message_post.py index 9e260bd8bb6..0d3aa00f546 100644 --- a/addons/test_mail/tests/test_message_post.py +++ b/addons/test_mail/tests/test_message_post.py @@ -1583,19 +1583,14 @@ class TestMessagePostLang(TestMailCommon, TestRecipients): self.assertIn('NotificationButtonTitle', body, 'Groups-based action names translation failed') else: - self.assertIn('Spanish Layout para', body, 'Layout content should be translated') - self.assertNotIn('English Layout for', body) - self.assertIn('Spanish Layout para Spanish Model Description', body, 'Model name should be translated') - # check notification layout strings - self.assertIn('View Lang Chatter Model', body, - 'Fixme: "View document" should be translated') - # self.assertIn('SpanishView Spanish Model Description', body, - # '"View document" should be translated') - # self.assertNotIn(f'View {test_record._description}', body, - # '"View document" should be translated') - # self.assertIn('SpanishButtonTitle', body, - # 'Groups-based action names should be translated') - self.assertIn('NotificationButtonTitle', body, 'Fixme: Groups-based action names should be translated') + self.assertIn('SpanishView Spanish Model Description', body, + '"View document" should be translated') + self.assertNotIn(f'View {test_record._description}', body, + '"View document" should be translated') + self.assertIn('SpanishButtonTitle', body, + 'Groups-based action names should be translated') + self.assertNotIn('NotificationButtonTitle', body, + 'Groups-based action names should be translated') @users('employee') @mute_logger('odoo.addons.mail.models.mail_mail') @@ -1654,7 +1649,8 @@ class TestMessagePostLang(TestMailCommon, TestRecipients): # check notification layout strings self.assertIn('SpanishView Spanish Model Description', body, '"View document" should be translated') - self.assertNotIn(f'View {test_records[1]._description}', body) + self.assertNotIn(f'View {test_records[1]._description}', body, + '"View document" should be translated') self.assertIn('SpanishButtonTitle', body, 'Groups-based action names should be translated') self.assertNotIn('NotificationButtonTitle', body) @@ -1697,13 +1693,13 @@ class TestMessagePostLang(TestMailCommon, TestRecipients): self.assertNotIn('English Layout for', body) self.assertIn('Spanish Layout para Spanish Model Description', body, 'Model name should be translated') - # self.assertIn('SpanishView Spanish Model Description', body, - # '"View document" should be translated') - self.assertIn(f'View {test_records[1]._description}', body, - 'Fixme: "View document" should be translated') - # self.assertIn('NotificationButtonTitle', body, - # 'Groups-based action names should be translated') - self.assertIn('NotificationButtonTitle', body, + self.assertIn('SpanishView Spanish Model Description', body, + '"View document" should be translated') + self.assertNotIn(f'View {test_records[1]._description}', body, + '"View document" should be translated') + self.assertIn('SpanishButtonTitle', body, + 'Groups-based action names should be translated') + self.assertNotIn('NotificationButtonTitle', body, 'Fixme: groups-based action names should be translated') @users('employee') diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py index 7a57ab192a0..ce57c95d01e 100644 --- a/addons/test_mail/tests/test_performance.py +++ b/addons/test_mail/tests/test_performance.py @@ -369,7 +369,7 @@ class TestMailAPIPerformance(BaseMailPerformance): 'partner_ids': [(4, customer.id)], }) - with self.assertQueryCount(admin=37, employee=37): + with self.assertQueryCount(admin=35, employee=35): composer._action_send_mail() @users('admin', 'employee') @@ -393,7 +393,7 @@ class TestMailAPIPerformance(BaseMailPerformance): composer_form.attachment_ids.add(attachment) composer = composer_form.save() - with self.assertQueryCount(admin=48, employee=48): # tm+com 47/47 + with self.assertQueryCount(admin=46, employee=46): # tm+com 45/45 composer._action_send_mail() # notifications @@ -479,7 +479,7 @@ class TestMailAPIPerformance(BaseMailPerformance): 'default_template_id': test_template.id, }).create({}) - with self.assertQueryCount(admin=44, employee=44): + with self.assertQueryCount(admin=42, employee=42): composer._action_send_mail() # notifications @@ -511,7 +511,7 @@ class TestMailAPIPerformance(BaseMailPerformance): ) composer = composer_form.save() - with self.assertQueryCount(admin=44, employee=44): + with self.assertQueryCount(admin=43, employee=43): composer._action_send_mail() # notifications @@ -541,7 +541,7 @@ class TestMailAPIPerformance(BaseMailPerformance): ) composer = composer_form.save() - with self.assertQueryCount(admin=64, employee=64): + with self.assertQueryCount(admin=61, employee=61): composer._action_send_mail() # notifications @@ -1309,7 +1309,7 @@ class TestMailHeavyPerformancePost(BaseMailPerformance): attachments = self.env['ir.attachment'].with_user(self.env.user).create(self.test_attachments_vals) # enable_logging = self.cr._enable_logging() if self.warm else nullcontext() # with self.assertQueryCount(employee=63), enable_logging: - with self.assertQueryCount(employee=63): + with self.assertQueryCount(employee=60): record_container.with_context({}).message_post( body='

Test body

', subject='Test Subject',