diff --git a/addons/mail/__manifest__.py b/addons/mail/__manifest__.py index af4b10a36ca..326edbfacf0 100644 --- a/addons/mail/__manifest__.py +++ b/addons/mail/__manifest__.py @@ -2,7 +2,7 @@ { 'name': 'Discuss', - 'version': '1.3', + 'version': '1.4', 'category': 'Productivity/Discuss', 'sequence': 145, 'summary': 'Chat, mail gateway and private channels', diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py index af9e3b57469..68b642fac90 100644 --- a/addons/mail/models/mail_template.py +++ b/addons/mail/models/mail_template.py @@ -43,7 +43,7 @@ class MailTemplate(models.Model): partner_to = fields.Char('To (Partners)', help="Comma-separated ids of recipient partners (placeholders may be used here)") email_cc = fields.Char('Cc', help="Carbon copy recipients (placeholders may be used here)") - reply_to = fields.Char('Reply-To', help="Preferred response address (placeholders may be used here)") + reply_to = fields.Char('Reply To', help="Email address to which replies will be redirected when sending emails in mass; only used when the reply is not logged in the original discussion thread.") # content body_html = fields.Html('Body', translate=True, sanitize=False) attachment_ids = fields.Many2many('ir.attachment', 'email_template_attachment_rel', 'email_template_id', diff --git a/addons/mail/views/mail_template_views.xml b/addons/mail/views/mail_template_views.xml index 14381000377..7626f6fe867 100644 --- a/addons/mail/views/mail_template_views.xml +++ b/addons/mail/views/mail_template_views.xml @@ -59,7 +59,7 @@ + placeholder="Email address to which replies will be redirected when sending emails in mass"/> diff --git a/addons/mail/views/res_partner_views.xml b/addons/mail/views/res_partner_views.xml index da5cc279ede..54aaaab18bb 100644 --- a/addons/mail/views/res_partner_views.xml +++ b/addons/mail/views/res_partner_views.xml @@ -111,7 +111,7 @@ 'default_composition_mode': 'mass_mail', 'default_partner_to': '${object.id or \'\'}', 'default_use_template': False, - 'default_no_auto_thread': True, + 'default_reply_to_force_new': True, }"/> list diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index db33cad58af..4ff8e19e2f3 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -67,10 +67,10 @@ class MailComposer(models.TransientModel): result['model'] = self._context.get('active_model') if 'res_id' in fields and 'res_id' not in result: result['res_id'] = self._context.get('active_id') - if 'no_auto_thread' in fields and 'no_auto_thread' not in result and result.get('model'): + if 'reply_to_mode' in fields and 'reply_to_mode' not in result and result.get('model'): # doesn't support threading if result['model'] not in self.env or not hasattr(self.env[result['model']], 'message_post'): - result['no_auto_thread'] = True + result['reply_to_mode'] = 'new' if 'active_domain' in self._context: # not context.get() because we want to keep global [] domains result['active_domain'] = '%s' % self._context.get('active_domain') @@ -123,10 +123,15 @@ class MailComposer(models.TransientModel): 'mail.activity.type', 'Mail Activity Type', index=True, ondelete='set null') # destination - reply_to = fields.Char('Reply-To', help='Reply email address. Setting the reply_to bypasses the automatic thread creation.') - no_auto_thread = fields.Boolean( - 'No threading for answers', - help='Answers do not go in the original document discussion thread. This has an impact on the generated message-id.') + reply_to = fields.Char('Reply To', help='Reply email address. Setting the reply_to bypasses the automatic thread creation.') + reply_to_force_new = fields.Boolean( + string='Considers answers as new thread', + help='Manage answers as new incoming emails instead of replies going to the same thread.') + reply_to_mode = fields.Selection([ + ('update', 'Log in the original discussion thread'), + ('new', 'Redirect to another email address')], + string='Replies', compute='_compute_reply_to_mode', inverse='_inverse_reply_to_mode', + help="Original Discussion: Answers go in the original document discussion thread. \n Another Email Address: Answers go to the email address mentioned in the tracking message-id instead of original document discussion thread. \n This has an impact on the generated message-id.") is_log = fields.Boolean('Log an Internal Note', help='Whether the message is an internal note (comment mode only)') partner_ids = fields.Many2many( @@ -140,6 +145,15 @@ class MailComposer(models.TransientModel): auto_delete_message = fields.Boolean('Delete Message Copy', help='Do not keep a copy of the email in the document communication history (mass mailing only)') mail_server_id = fields.Many2one('ir.mail_server', 'Outgoing mail server') + @api.depends('reply_to_force_new') + def _compute_reply_to_mode(self): + for composer in self: + composer.reply_to_mode = 'new' if composer.reply_to_force_new else 'update' + + def _inverse_reply_to_mode(self): + for composer in self: + composer.reply_to_force_new = composer.reply_to_mode == 'new' + @api.model def get_record_data(self, values): """ Returns a defaults-like dict with initial values for the composition @@ -275,7 +289,7 @@ class MailComposer(models.TransientModel): rendered_values = self.render_message(res_ids) # compute alias-based reply-to in batch reply_to_value = dict.fromkeys(res_ids, None) - if mass_mail_mode and not self.no_auto_thread: + if mass_mail_mode and not self.reply_to_force_new: records = self.env[self.model].browse(res_ids) reply_to_value = records._notify_get_reply_to(default=self.email_from) @@ -301,7 +315,7 @@ class MailComposer(models.TransientModel): 'author_id': self.author_id.id, 'email_from': self.email_from, 'record_name': self.record_name, - 'no_auto_thread': self.no_auto_thread, + 'no_auto_thread': self.reply_to_force_new, 'mail_server_id': self.mail_server_id.id, 'mail_activity_type_id': self.mail_activity_type_id.id, } @@ -319,11 +333,11 @@ class MailComposer(models.TransientModel): email_dict = rendered_values[res_id] mail_values['partner_ids'] += email_dict.pop('partner_ids', []) mail_values.update(email_dict) - if not self.no_auto_thread: + if not self.reply_to_force_new: mail_values.pop('reply_to') if reply_to_value.get(res_id): mail_values['reply_to'] = reply_to_value[res_id] - if self.no_auto_thread and not mail_values.get('reply_to'): + if self.reply_to_force_new and not mail_values.get('reply_to'): mail_values['reply_to'] = mail_values['email_from'] # mail_mail values: body -> body_html, partner_ids -> recipient_ids mail_values['body_html'] = mail_values.get('body', '') diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index 95a78247852..d8cac0c74c2 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -62,10 +62,11 @@ - - + + + diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index c4e3c1e9724..b8dabfe112b 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -631,7 +631,7 @@ class MassMailing(models.Model): 'composition_mode': 'mass_mail', 'mass_mailing_id': mailing.id, 'mailing_list_ids': [(4, l.id) for l in mailing.contact_list_ids], - 'no_auto_thread': mailing.reply_to_mode != 'thread', + 'reply_to_force_new': mailing.reply_to_mode != 'thread', 'template_id': None, 'mail_server_id': mailing.mail_server_id.id, } diff --git a/addons/mass_mailing/wizard/mail_compose_message.py b/addons/mass_mailing/wizard/mail_compose_message.py index 19a345a9e47..256b68398bd 100644 --- a/addons/mass_mailing/wizard/mail_compose_message.py +++ b/addons/mass_mailing/wizard/mail_compose_message.py @@ -25,20 +25,18 @@ class MailComposeMessage(models.TransientModel): self.env['ir.model'].sudo().search([('model', '=', self.model), ('is_mail_thread', '=', True)], limit=1): mass_mailing = self.mass_mailing_id if not mass_mailing: - reply_to_mode = 'email' if self.no_auto_thread else 'thread' - reply_to = self.reply_to if self.no_auto_thread else False 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': reply_to_mode, - 'reply_to': reply_to, - 'sent_date': fields.Datetime.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)], + '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 == 'email' else False, + 'sent_date': fields.Datetime.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)], }) # Preprocess res.partners to batch-fetch from db diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py index 6c60ea3d293..17f8e1f5afb 100644 --- a/addons/test_mail/tests/test_mail_composer.py +++ b/addons/test_mail/tests/test_mail_composer.py @@ -110,7 +110,7 @@ class TestComposerForm(TestMailComposer): 'MailComposer: comment mode should have default subject Re: record_name') # record name not displayed currently in view # self.assertEqual(composer_form.record_name, self.test_record.name, 'MailComposer: comment mode should compute record name') - self.assertFalse(composer_form.no_auto_thread) + self.assertFalse(composer_form.reply_to_force_new) self.assertEqual(composer_form.composition_mode, 'comment') self.assertEqual(composer_form.model, self.test_record._name) @@ -172,7 +172,7 @@ class TestComposerForm(TestMailComposer): self.assertFalse(composer_form.subject, 'MailComposer: mass mode should have void default subject if no template') # record name not displayed currently in view # self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name') - self.assertFalse(composer_form.no_auto_thread) + self.assertFalse(composer_form.reply_to_force_new) self.assertEqual(composer_form.composition_mode, 'mass_mail') self.assertEqual(composer_form.model, self.test_records._name) @@ -186,7 +186,7 @@ class TestComposerForm(TestMailComposer): 'MailComposer: mass mode should have template raw body if template') # record name not displayed currently in view # self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name') - self.assertFalse(composer_form.no_auto_thread) + self.assertFalse(composer_form.reply_to_force_new) self.assertEqual(composer_form.composition_mode, 'mass_mail') self.assertEqual(composer_form.model, self.test_records._name)