Merge pull request #683 from odoo-dev/master-ref-reply_to-tde
[REF] mail, crm, hr_recruitment, project, project_issue: simplified repl.y-to of notification emails (now displaying 'YourCompany -docname- ' instead of 'Followers of ...'; added same_thread directly on the mail.message model to ease the reply_to and message-id computation; factorized a bit the reply-to generation for a batch of records; udpated tests accordingly.
This commit is contained in:
commit
5739f3a2f9
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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', '')
|
||||
|
|
|
@ -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. """
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue