[REF] mail, crm, hr_recruitment, project, project_issue: simplified reply-to of notif...
authorThibault Delavallée <tde@openerp.com>
Fri, 20 Jun 2014 11:38:22 +0000 (13:38 +0200)
committerThibault Delavallée <tde@openerp.com>
Mon, 23 Jun 2014 08:47:47 +0000 (10:47 +0200)
addons/crm/crm_lead.py
addons/email_template/tests/test_mail.py
addons/hr_recruitment/hr_recruitment.py
addons/mail/mail_mail.py
addons/mail/mail_message.py
addons/mail/mail_thread.py
addons/mail/tests/test_mail_features.py
addons/mail/tests/test_mail_message.py
addons/mail/wizard/mail_compose_message.py
addons/project/project.py
addons/project_issue/project_issue.py

index 0e0b734..305ad54 100644 (file)
@@ -935,8 +935,10 @@ class crm_lead(format_address, osv.osv):
 
     def message_get_reply_to(self, cr, uid, ids, context=None):
         """ Override to get the reply_to of the parent project. """
-        return [lead.section_id.message_get_reply_to()[0] if lead.section_id else False
-                    for lead in self.browse(cr, SUPERUSER_ID, ids, context=context)]
+        leads = self.browse(cr, SUPERUSER_ID, ids, context=context)
+        section_ids = set([lead.section_id.id for lead in leads if lead.section_id])
+        aliases = self.pool['crm.case.section'].message_get_reply_to(cr, uid, list(section_ids), context=context)
+        return dict((lead.id, aliases.get(lead.section_id and lead.section_id.id or 0, False)) for lead in leads)
 
     def get_formview_id(self, cr, uid, id, context=None):
         obj = self.browse(cr, uid, id, context=context)
index beda74d..f88d11e 100644 (file)
@@ -237,8 +237,8 @@ class test_message_compose(TestMail):
         email_template.send_mail(cr, uid, email_template_id, self.group_pigs_id, force_send=True, context=context)
         sent_emails = self._build_email_kwargs_list
         email_to_lst = [
-            ['b@b.b', 'c@c.c'], ['Administrator <admin@yourcompany.example.com>'],
-            ['Raoul Grosbedon <raoul@raoul.fr>'], ['Bert Tartignole <bert@bert.fr>']]
+            ['b@b.b', 'c@c.c'], ['"Administrator" <admin@yourcompany.example.com>'],
+            ['"Raoul Grosbedon" <raoul@raoul.fr>'], ['"Bert Tartignole" <bert@bert.fr>']]
         self.assertEqual(len(sent_emails), 4, 'email_template: send_mail: 3 valid email recipients + email_to -> should send 4 emails')
         for email in sent_emails:
             self.assertIn(email['email_to'], email_to_lst, 'email_template: send_mail: wrong email_recipients')
index 775b847..e0c3abf 100644 (file)
@@ -20,6 +20,8 @@
 ##############################################################################
 
 from datetime import datetime
+
+from openerp import SUPERUSER_ID
 from openerp.osv import fields, osv
 from openerp.tools.translate import _
 
@@ -357,6 +359,13 @@ class hr_applicant(osv.Model):
         action['domain'] = str(['&', ('res_model', '=', self._name), ('res_id', 'in', ids)])
         return action
 
+    def message_get_reply_to(self, cr, uid, ids, context=None):
+        """ Override to get the reply_to of the parent project. """
+        applicants = self.browse(cr, SUPERUSER_ID, ids, context=context)
+        job_ids = set([applicant.job_id.id for applicant in applicants if applicant.job_id])
+        aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(job_ids), context=context)
+        return dict((applicant.id, aliases.get(applicant.job_id and applicant.job_id.id or 0, False)) for applicant in applicants)
+
     def message_get_suggested_recipients(self, cr, uid, ids, context=None):
         recipients = super(hr_applicant, self).message_get_suggested_recipients(cr, uid, ids, context=context)
         for applicant in self.browse(cr, uid, ids, context=context):
index fc320e0..0a6a250 100644 (file)
@@ -188,11 +188,8 @@ class mail_mail(osv.Model):
           - if 'partner' and mail is a notification on a document: followers (Followers of 'Doc' <email>)
           - elif 'partner', no notificatoin or no doc: recipient specific (Partner Name <email>)
           - else fallback on mail.email_to splitting """
-        if partner and mail.notification and mail.record_name:
-            sanitized_record_name = re.sub(r'[^\w+.]+', '-', mail.record_name)
-            email_to = [_('"Followers of %s" <%s>') % (sanitized_record_name, partner.email)]
-        elif partner:
-            email_to = ['%s <%s>' % (partner.name, partner.email)]
+        if partner:
+            email_to = ['"%s" <%s>' % (partner.name, partner.email)]
         else:
             email_to = tools.email_split(mail.email_to)
         return email_to
index 5b716a1..b924bb6 100644 (file)
@@ -20,7 +20,6 @@
 ##############################################################################
 
 import logging
-import re
 
 from openerp import tools
 
@@ -131,6 +130,8 @@ class mail_message(osv.Model):
             help="Email address of the sender. This field is set when no matching partner is found for incoming emails."),
         'reply_to': fields.char('Reply-To',
             help='Reply email address. Setting the reply_to bypasses the automatic thread creation.'),
+        'same_thread': fields.boolean('Same thread',
+            help='Redirect answers to the same discussion thread.'),
         'author_id': fields.many2one('res.partner', 'Author', select=1,
             ondelete='set null',
             help="Author of the message. If not set, email_from may hold an email address that did not match any partner."),
@@ -188,6 +189,7 @@ class mail_message(osv.Model):
         'author_id': lambda self, cr, uid, ctx=None: self._get_default_author(cr, uid, ctx),
         'body': '',
         'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx),
+        'same_thread': True,
     }
 
     #------------------------------------------------------
@@ -766,42 +768,12 @@ class mail_message(osv.Model):
         """ Return a specific reply_to: alias of the document through message_get_reply_to
             or take the email_from
         """
-        email_reply_to = None
-
-        ir_config_parameter = self.pool.get("ir.config_parameter")
-        catchall_domain = ir_config_parameter.get_param(cr, uid, "mail.catchall.domain", context=context)
-
-        # model, res_id, email_from: comes from values OR related message
         model, res_id, email_from = values.get('model'), values.get('res_id'), values.get('email_from')
-
-        # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias
-        if not email_reply_to and model and res_id and catchall_domain and hasattr(self.pool[model], 'message_get_reply_to'):
-            email_reply_to = self.pool[model].message_get_reply_to(cr, uid, [res_id], context=context)[0]
-        # no alias reply_to -> catchall alias
-        if not email_reply_to and catchall_domain:
-            catchall_alias = ir_config_parameter.get_param(cr, uid, "mail.catchall.alias", context=context)
-            if catchall_alias:
-                email_reply_to = '%s@%s' % (catchall_alias, catchall_domain)
-        # still no reply_to -> reply_to will be the email_from
-        if not email_reply_to and email_from:
-            email_reply_to = email_from
-
-        # format 'Document name <email_address>'
-        if email_reply_to and model and res_id:
-            emails = tools.email_split(email_reply_to)
-            if emails:
-                email_reply_to = emails[0]
-            document_name = self.pool[model].name_get(cr, SUPERUSER_ID, [res_id], context=context)[0]
-            if document_name:
-                # sanitize document name
-                sanitized_doc_name = re.sub(r'[^\w+.]+', '-', document_name[1])
-                # generate reply to
-                email_reply_to = _('"Followers of %s" <%s>') % (sanitized_doc_name, email_reply_to)
-
-        return email_reply_to
+        ctx = dict(context, thread_model=model)
+        return self.pool['mail.thread'].message_get_reply_to(cr, uid, [res_id], default=email_from, context=ctx)[res_id]
 
     def _get_message_id(self, cr, uid, values, context=None):
-        if values.get('reply_to'):
+        if values.get('same_thread', True) is False:
             message_id = tools.generate_tracking_message_id('reply_to')
         elif values.get('res_id') and values.get('model'):
             message_id = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values)
index 7c3fb84..54f2b8d 100644 (file)
@@ -31,6 +31,7 @@ except ImportError:
 from lxml import etree
 import logging
 import pytz
+import re
 import socket
 import time
 import xmlrpclib
@@ -688,14 +689,50 @@ class mail_thread(osv.AbstractModel):
             res[record.id] = {'partner_ids': list(recipient_ids), 'email_to': email_to, 'email_cc': email_cc}
         return res
 
-    def message_get_reply_to(self, cr, uid, ids, context=None):
+    def message_get_reply_to(self, cr, uid, ids, default=None, context=None):
         """ Returns the preferred reply-to email address that is basically
             the alias of the document, if it exists. """
-        if not self._inherits.get('mail.alias'):
-            return [False for id in ids]
-        return ["%s@%s" % (record.alias_name, record.alias_domain)
-                if record.alias_domain and record.alias_name else False
-                for record in self.browse(cr, SUPERUSER_ID, ids, context=context)]
+        if context is None:
+            context = {}
+        model_name = context.get('thread_model') or self._name
+        alias_domain = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.domain", context=context)
+        res = dict.fromkeys(ids, False)
+
+        # alias domain: check for aliases and catchall
+        aliases = {}
+        doc_names = {}
+        if alias_domain:
+            if model_name and model_name != 'mail.thread':
+                alias_ids = self.pool['mail.alias'].search(
+                    cr, SUPERUSER_ID, [
+                        ('alias_parent_model_id.model', '=', model_name),
+                        ('alias_parent_thread_id', 'in', ids),
+                        ('alias_name', '!=', False)
+                    ], context=context)
+                aliases.update(
+                    dict((alias.alias_parent_thread_id, '%s@%s' % (alias.alias_name, alias_domain))
+                         for alias in self.pool['mail.alias'].browse(cr, SUPERUSER_ID, alias_ids, context=context)))
+                doc_names.update(
+                    dict((ng_res[0], ng_res[1])
+                         for ng_res in self.pool[model_name].name_get(cr, SUPERUSER_ID, aliases.keys(), context=context)))
+            # left ids: use catchall
+            left_ids = set(ids).difference(set(aliases.keys()))
+            if left_ids:
+                catchall_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.alias", context=context)
+                if catchall_alias:
+                    aliases.update(dict((res_id, '%s@%s' % (catchall_alias, alias_domain)) for res_id in left_ids))
+            # compute name of reply-to
+            company_name = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).company_id.name
+            res.update(
+                dict((res_id, '"%(company_name)s%(document_name)s" <%(email)s>' %
+                     {'company_name': company_name,
+                      'document_name': doc_names.get(res_id) and ' ' + re.sub(r'[^\w+.]+', '-', doc_names[res_id]) or '',
+                      'email': aliases[res_id]
+                      } or False) for res_id in aliases.keys()))
+        left_ids = set(ids).difference(set(aliases.keys()))
+        if left_ids and default:
+            res.update(dict((res_id, default) for res_id in left_ids))
+        return res
 
     def message_get_email_values(self, cr, uid, id, notif_mail=None, context=None):
         """ Get specific notification email values to store on the notification
index 0a481c7..17836ea 100644 (file)
@@ -449,8 +449,8 @@ class test_mail(TestMail):
                         'message_post: mail.mail notifications should have been auto-deleted!')
 
         # Test: notifications emails: to a and b, c is email only, r is author
-        # test_emailto = ['Administrator <a@a>', 'Bert Tartopoils <b@b>']
-        test_emailto = ['"Followers of -Pigs-" <a@a>', '"Followers of -Pigs-" <b@b>']
+        test_emailto = ['"Administrator" <a@a>', '"Bert Tartopoils" <b@b>']
+        # test_emailto = ['"Followers of -Pigs-" <a@a>', '"Followers of -Pigs-" <b@b>']
         self.assertEqual(len(sent_emails), 2,
                         'message_post: notification emails wrong number of send emails')
         self.assertEqual(set([m['email_to'][0] for m in sent_emails]), set(test_emailto),
@@ -462,7 +462,7 @@ class test_mail(TestMail):
                             'message_post: notification email sent to more than one email address instead of a precise partner')
             self.assertIn(sent_email['email_to'][0], test_emailto,
                             'message_post: notification email email_to incorrect')
-            self.assertEqual(sent_email['reply_to'], '"Followers of -Pigs-" <group+pigs@schlouby.fr>',
+            self.assertEqual(sent_email['reply_to'], '"YourCompany -Pigs-" <group+pigs@schlouby.fr>',
                             'message_post: notification email reply_to incorrect')
             self.assertEqual(_subject, sent_email['subject'],
                             'message_post: notification email subject incorrect')
@@ -523,8 +523,8 @@ class test_mail(TestMail):
         self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg2_id)]), 'mail.mail notifications should have been auto-deleted!')
 
         # Test: emails send by server (to a, b, c, d)
-        # test_emailto = [u'Administrator <a@a>', u'Bert Tartopoils <b@b>', u'Carine Poilvache <c@c>', u'D\xe9d\xe9 Grosbedon <d@d>']
-        test_emailto = [u'"Followers of Pigs" <a@a>', u'"Followers of Pigs" <b@b>', u'"Followers of Pigs" <c@c>', u'"Followers of Pigs" <d@d>']
+        test_emailto = [u'"Administrator" <a@a>', u'"Bert Tartopoils" <b@b>', u'"Carine Poilvache" <c@c>', u'"D\xe9d\xe9 Grosbedon" <d@d>']
+        # test_emailto = [u'"Followers of Pigs" <a@a>', u'"Followers of Pigs" <b@b>', u'"Followers of Pigs" <c@c>', u'"Followers of Pigs" <d@d>']
         # self.assertEqual(len(sent_emails), 3, 'sent_email number of sent emails incorrect')
         for sent_email in sent_emails:
             self.assertEqual(sent_email['email_from'], 'Raoul Grosbedon <r@r>',
index 180661a..d4a7ae7 100644 (file)
@@ -81,8 +81,7 @@ class TestMailMessage(TestMail):
         alias_domain = 'schlouby.fr'
         raoul_from = 'Raoul Grosbedon <raoul@raoul.fr>'
         raoul_from_alias = 'Raoul Grosbedon <raoul@schlouby.fr>'
-        raoul_reply = '"Followers of Pigs" <raoul@raoul.fr>'
-        raoul_reply_alias = '"Followers of Pigs" <group+pigs@schlouby.fr>'
+        raoul_reply_alias = '"YourCompany Pigs" <group+pigs@schlouby.fr>'
 
         # --------------------------------------------------
         # Case1: without alias_domain
@@ -91,7 +90,7 @@ class TestMailMessage(TestMail):
         self.registry('ir.config_parameter').unlink(cr, uid, param_ids)
 
         # Do: free message; specified values > default values
-        msg_id = self.mail_message.create(cr, user_raoul_id, {'reply_to': reply_to1, 'email_from': email_from1})
+        msg_id = self.mail_message.create(cr, user_raoul_id, {'same_thread': False, 'reply_to': reply_to1, 'email_from': email_from1})
         msg = self.mail_message.browse(cr, user_raoul_id, msg_id)
         # Test: message content
         self.assertIn('reply_to', msg.message_id,
@@ -118,7 +117,7 @@ class TestMailMessage(TestMail):
                       'mail_message: message_id should contain model')
         self.assertIn('%s' % self.group_pigs_id, msg.message_id,
                       'mail_message: message_id should contain res_id')
-        self.assertEqual(msg.reply_to, raoul_reply,
+        self.assertEqual(msg.reply_to, raoul_from,
                          'mail_message: incorrect reply_to: should be Raoul')
         self.assertEqual(msg.email_from, raoul_from,
                          'mail_message: incorrect email_from: should be Raoul')
@@ -152,7 +151,7 @@ class TestMailMessage(TestMail):
         msg_id = self.mail_message.create(cr, user_raoul_id, {})
         msg = self.mail_message.browse(cr, user_raoul_id, msg_id)
         # Test: generated reply_to
-        self.assertEqual(msg.reply_to, 'gateway@schlouby.fr',
+        self.assertEqual(msg.reply_to, '"YourCompany" <gateway@schlouby.fr>',
                          'mail_mail: reply_to should equal the catchall email alias')
 
         # Do: create a mail_mail
index e14a8f3..0425cd1 100644 (file)
@@ -121,16 +121,12 @@ class mail_compose_message(osv.TransientModel):
         # mass mode options
         'notify': fields.boolean('Notify followers',
             help='Notify followers of the document (mass post only)'),
-        'same_thread': fields.boolean('Replies in the document',
-            help='Replies to the messages will go into the selected document (mass mail only)'),
     }
-    #TODO change same_thread to False in trunk (Require view update)
     _defaults = {
         'composition_mode': 'comment',
         'body': lambda self, cr, uid, ctx={}: '',
         'subject': lambda self, cr, uid, ctx={}: False,
         'partner_ids': lambda self, cr, uid, ctx={}: [],
-        'same_thread': True,
     }
 
     def check_access_rule(self, cr, uid, ids, operation, context=None):
@@ -251,6 +247,10 @@ class mail_compose_message(osv.TransientModel):
         # render all template-based value at once
         if mass_mail_mode and wizard.model:
             rendered_values = self.render_message_batch(cr, uid, wizard, res_ids, context=context)
+        # compute alias-based reply-to in batch
+        reply_to_value = dict.fromkeys(res_ids, None)
+        if mass_mail_mode and wizard.same_thread:
+            reply_to_value = self.pool['mail.thread'].message_get_reply_to(cr, uid, res_ids, default=wizard.email_from, context=dict(context, thread_model=wizard.model))
 
         for res_id in res_ids:
             # static wizard (mail.message) values
@@ -277,7 +277,9 @@ class mail_compose_message(osv.TransientModel):
                 mail_values.update(email_dict)
                 if wizard.same_thread:
                     mail_values.pop('reply_to')
-                elif not mail_values.get('reply_to'):
+                    if reply_to_value.get(res_id):
+                        mail_values['reply_to'] = reply_to_value[res_id]
+                if not wizard.same_thread 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', '')
index a9c487b..8f66535 100644 (file)
@@ -1099,8 +1099,10 @@ class task(osv.osv):
 
     def message_get_reply_to(self, cr, uid, ids, context=None):
         """ Override to get the reply_to of the parent project. """
-        return [task.project_id.message_get_reply_to()[0] if task.project_id else False
-                    for task in self.browse(cr, uid, ids, context=context)]
+        tasks = self.browse(cr, SUPERUSER_ID, ids, context=context)
+        project_ids = set([task.project_id.id for task in tasks if task.project_id])
+        aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(project_ids), context=context)
+        return dict((task.id, aliases.get(task.project_id and task.project_id.id or 0, False)) for task in tasks)
 
     def message_new(self, cr, uid, msg, custom_values=None, context=None):
         """ Override to updates the document according to the email. """
index 6c06f14..9bbcd43 100644 (file)
@@ -414,8 +414,10 @@ class project_issue(osv.Model):
 
     def message_get_reply_to(self, cr, uid, ids, context=None):
         """ Override to get the reply_to of the parent project. """
-        return [issue.project_id.message_get_reply_to()[0] if issue.project_id else False
-                    for issue in self.browse(cr, uid, ids, context=context)]
+        issues = self.browse(cr, SUPERUSER_ID, ids, context=context)
+        project_ids = set([issue.project_id.id for issue in issues if issue.project_id])
+        aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(project_ids), context=context)
+        return dict((issue.id, aliases.get(issue.project_id and issue.project_id.id or 0, False)) for issue in issues)
 
     def message_get_suggested_recipients(self, cr, uid, ids, context=None):
         recipients = super(project_issue, self).message_get_suggested_recipients(cr, uid, ids, context=context)