summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibault Delavallée <tde@odoo.com>2019-04-24 10:25:09 +0000
committerThibault Delavallée <tde@odoo.com>2019-11-29 13:35:14 +0000
commit1838191eece59051805862ed6ce914bf5e5d1efb (patch)
tree75955be7412a59bd7463bf4b61116b5711141352
parentb9a22d2f24e587894c12bf7ad289fcddcea8a823 (diff)
[REF] mail, various: improve mail creation calls, notably author and email from default computation
Purpose of this commit is to correctly compute author_id and email_from in mail_message and mail_mail as they depends from each other. Moreover it is a good idea in various flows to specify email and author when giving creation values to avoid default computation that is not always guaranteed to be accurate notably when involving super user. Mail message creation could lead to desynchronized values between author and email_from. This is improved with this commit by correctly inheriting from default_get and computing both of them at the same time instead of having two default values. Indeed they depend on each other. Same thing is done for mail composer. Mail Thread offers a tool method to find email_from / author_id based on having one of those values or current user and it is called whenever necessary. Some calls to mail template send_mail are also cleaned. Task ID 1853147 PR #32243
-rw-r--r--addons/auth_signup/controllers/main.py5
-rw-r--r--addons/auth_signup/data/auth_signup_data.xml2
-rw-r--r--addons/auth_signup/models/res_users.py3
-rw-r--r--addons/hr_expense/models/hr_expense.py2
-rw-r--r--addons/im_livechat/models/mail_channel.py3
-rw-r--r--addons/mail/models/mail_channel.py5
-rw-r--r--addons/mail/models/mail_message.py41
-rw-r--r--addons/mail/models/mail_thread.py3
-rw-r--r--addons/mail/wizard/invite.py5
-rw-r--r--addons/mail/wizard/mail_compose_message.py16
-rw-r--r--addons/mass_mailing/models/mailing.py4
-rw-r--r--addons/mass_mailing/wizard/mailing_mailing_test.py4
-rw-r--r--addons/payment_paypal/models/payment.py3
-rw-r--r--addons/website_slides/models/slide_slide.py14
14 files changed, 63 insertions, 47 deletions
diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py
index 986278e2ab3..3b6b92afd54 100644
--- a/addons/auth_signup/controllers/main.py
+++ b/addons/auth_signup/controllers/main.py
@@ -40,10 +40,7 @@ class AuthSignupHome(Home):
user_sudo = request.env['res.users'].sudo().search([('login', '=', qcontext.get('login'))])
template = request.env.ref('auth_signup.mail_template_user_signup_account_created', raise_if_not_found=False)
if user_sudo and template:
- template.sudo().with_context(
- lang=user_sudo.lang,
- auth_login=werkzeug.url_encode({'auth_login': user_sudo.email}),
- ).send_mail(user_sudo.id, force_send=True)
+ template.sudo().send_mail(user_sudo.id, force_send=True)
return self.web_login(*args, **kw)
except UserError as e:
qcontext['error'] = e.name or e.value
diff --git a/addons/auth_signup/data/auth_signup_data.xml b/addons/auth_signup/data/auth_signup_data.xml
index 5fe062a6d44..e83930f0b88 100644
--- a/addons/auth_signup/data/auth_signup_data.xml
+++ b/addons/auth_signup/data/auth_signup_data.xml
@@ -289,7 +289,7 @@
Your login is <strong>${object.email}</strong><br/>
To gain access to your account, you can use the following link:
<div style="margin: 16px 0px 16px 0px;">
- <a href="/web/login?${ctx['auth_login']}"
+ <a href="/web/login?auth_login=${object.email}"
style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;">
Go to My Account
</a>
diff --git a/addons/auth_signup/models/res_users.py b/addons/auth_signup/models/res_users.py
index 11df8c34116..a1abd1c32b6 100644
--- a/addons/auth_signup/models/res_users.py
+++ b/addons/auth_signup/models/res_users.py
@@ -195,8 +195,9 @@ class ResUsers(models.Model):
for user in self:
if not user.email:
raise UserError(_("Cannot send email: user %s has no email address.") % user.name)
+ # TDE FIXME: make this template technical (qweb)
with self.env.cr.savepoint():
- template.with_context(lang=user.lang).send_mail(user.id, force_send=True, raise_exception=True)
+ template.send_mail(user.id, force_send=True, raise_exception=True)
_logger.info("Password reset email sent for user <%s> to <%s>", user.login, user.email)
def send_unregistered_user_reminder(self, after_days=5):
diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py
index a535a8840c0..259cb7e0167 100644
--- a/addons/hr_expense/models/hr_expense.py
+++ b/addons/hr_expense/models/hr_expense.py
@@ -617,6 +617,8 @@ class HrExpense(models.Model):
)
else:
self.env['mail.mail'].create({
+ 'email_from': self.env.user.email_formatted,
+ 'author_id': self.env.user.partner_id.id,
'body_html': body,
'subject': 'Re: %s' % msg_dict.get('subject', ''),
'email_to': msg_dict.get('email_from', False),
diff --git a/addons/im_livechat/models/mail_channel.py b/addons/im_livechat/models/mail_channel.py
index a46bc56d4d7..b5397e38bdf 100644
--- a/addons/im_livechat/models/mail_channel.py
+++ b/addons/im_livechat/models/mail_channel.py
@@ -173,7 +173,8 @@ class MailChannel(models.Model):
mail_body = self.env['mail.thread']._replace_local_links(mail_body)
mail = self.env['mail.mail'].create({
'subject': _('Conversation with %s') % self.livechat_operator_id.name,
- 'email_from': self.env.company.email,
+ 'email_from': company.catchall_formatted or company.email_formatted,
+ 'author_id': self.env.user.partner_id.id,
'email_to': email,
'body_html': mail_body,
})
diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py
index a8440322c2b..ad1a1ef42b4 100644
--- a/addons/mail/models/mail_channel.py
+++ b/addons/mail/models/mail_channel.py
@@ -368,6 +368,8 @@ class Channel(models.Model):
# The fields "email_from" and "reply_to" are filled in automatically by method create in model mail.message.
if self.moderation_notify and self.moderation_notify_msg and message_type == 'email' and moderation_status == 'pending_moderation':
self.env['mail.mail'].create({
+ 'author_id': self.env.user.partner_id.id,
+ 'email_from': self.env.user.company_id.catchall_formatted or self.env.user.company_id.email_formatted,
'body_html': self.moderation_notify_msg,
'subject': 'Re: %s' % (kwargs.get('subject', '')),
'email_to': email,
@@ -418,9 +420,10 @@ class Channel(models.Model):
]).mapped('email')
for partner in partners.filtered(lambda p: p.email and not (p.email in banned_emails)):
create_values = {
+ 'email_from': partner.company_id.catchall_formatted or partner.company_id.email_formatted,
+ 'author_id': self.env.user.partner_id.id,
'body_html': view.render({'channel': self, 'partner': partner}, engine='ir.qweb', minimal_qcontext=True),
'subject': _("Guidelines of channel %s") % self.name,
- 'email_from': partner.company_id.catchall_formatted or partner.company_id.email,
'recipient_ids': [(4, partner.id)]
}
mail = self.env['mail.mail'].create(create_values)
diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py
index 0bcc082fee5..5be02450f87 100644
--- a/addons/mail/models/mail_message.py
+++ b/addons/mail/models/mail_message.py
@@ -9,7 +9,7 @@ from collections import defaultdict
from operator import itemgetter
from odoo import _, api, fields, models, modules, tools
-from odoo.exceptions import UserError, AccessError
+from odoo.exceptions import AccessError
from odoo.http import request
from odoo.osv import expression
from odoo.tools import groupby
@@ -27,14 +27,17 @@ class Message(models.Model):
_rec_name = 'record_name'
@api.model
- def _get_default_from(self):
- if self.env.user.email:
- return tools.formataddr((self.env.user.name, self.env.user.email))
- raise UserError(_("Unable to post message, please configure the sender's email address."))
-
- @api.model
- def _get_default_author(self):
- return self.env.user.partner_id
+ def default_get(self, fields):
+ res = super(Message, self).default_get(fields)
+ missing_author = 'author_id' in fields and 'author_id' not in res
+ missing_email_from = 'email_from' in fields and 'email_from' not in res
+ if missing_author or missing_email_from:
+ author_id, email_from = self.env['mail.thread']._message_compute_author(res.get('author_id'), res.get('email_from'), raise_exception=False)
+ if missing_email_from:
+ res['email_from'] = email_from
+ if missing_author:
+ res['author_id'] = author_id
+ return res
# content
subject = fields.Char('Subject')
@@ -69,12 +72,9 @@ class Message(models.Model):
'mail.activity.type', 'Mail Activity Type',
index=True, ondelete='set null')
# origin
- email_from = fields.Char(
- 'From', default=_get_default_from,
- help="Email address of the sender. This field is set when no matching partner is found and replaces the author_id field in the chatter.")
+ email_from = fields.Char('From', help="Email address of the sender. This field is set when no matching partner is found and replaces the author_id field in the chatter.")
author_id = fields.Many2one(
- 'res.partner', 'Author', index=True,
- ondelete='set null', default=_get_default_author,
+ 'res.partner', 'Author', index=True, ondelete='set null',
help="Author of the message. If not set, email_from may hold an email address that did not match any partner.")
author_avatar = fields.Binary("Author's avatar", related='author_id.image_128', readonly=False)
# recipients: include inactive partners (they may have been archived after
@@ -574,7 +574,8 @@ class Message(models.Model):
tracking_values_list = []
for values in values_list:
if 'email_from' not in values: # needed to compute reply_to
- values['email_from'] = self._get_default_from()
+ author_id, email_from = self.env['mail.thread']._message_compute_author(values.get('author_id'), email_from=None, raise_exception=False)
+ values['email_from'] = email_from
if not values.get('message_id'):
values['message_id'] = self._get_message_id(values)
if 'reply_to' not in values:
@@ -833,16 +834,12 @@ class Message(models.Model):
for msg in self:
if not msg.email_from:
continue
- if self.env.user.partner_id.email:
- email_from = tools.formataddr((self.env.user.partner_id.name, self.env.user.partner_id.email))
- else:
- email_from = self.env.company.catchall_formatted
-
body_html = tools.append_content_to_html('<div>%s</div>' % tools.ustr(comment), msg.body, plaintext=False)
vals = {
'subject': subject,
'body_html': body_html,
- 'email_from': email_from,
+ 'author_id': self.env.user.partner_id.id,
+ 'email_from': self.env.user.email_formatted or self.env.company_id.catchall_formatted,
'email_to': msg.email_from,
'auto_delete': True,
'state': 'outgoing'
@@ -924,7 +921,7 @@ class Message(models.Model):
partner_ids=moderator.partner_id.ids,
subject=_('Message are pending moderation'), # tocheck: target language
body=template.render({'record': moderator.partner_id}, engine='ir.qweb', minimal_qcontext=True),
- email_from=moderator.company_id.catchall_formatted or moderator.company_id.email,
+ email_from=moderator.company_id.catchall_formatted or moderator.company_id.email_formatted,
)
# ------------------------------------------------------
diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index 0c53cbad6b1..d0abc8adab7 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -685,6 +685,7 @@ class MailThread(models.AbstractModel):
def _routing_create_bounce_email(self, email_from, body_html, message, **mail_values):
bounce_to = tools.decode_message_header(message, 'Return-Path') or email_from
bounce_mail_values = {
+ 'author_id': False,
'body_html': body_html,
'subject': 'Re: %s' % message.get('subject'),
'email_to': bounce_to,
@@ -693,6 +694,8 @@ class MailThread(models.AbstractModel):
bounce_from = self.env['ir.mail_server']._get_default_bounce_address()
if bounce_from:
bounce_mail_values['email_from'] = tools.formataddr(('MAILER-DAEMON', bounce_from))
+ else:
+ bounce_mail_values['email_from'] = tools.decode_message_header(message, 'To')
bounce_mail_values.update(mail_values)
self.env['mail.mail'].create(bounce_mail_values).send()
diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py
index 44a68a92bb9..78bf03dd776 100644
--- a/addons/mail/wizard/invite.py
+++ b/addons/mail/wizard/invite.py
@@ -5,6 +5,7 @@ from lxml import etree
from lxml.html import builder as html
from odoo import _, api, fields, models
+from odoo.exceptions import UserError
class Invite(models.TransientModel):
@@ -47,7 +48,9 @@ class Invite(models.TransientModel):
send_mail = fields.Boolean('Send Email', default=True, help="If checked, the partners will receive an email warning they have been added in the document's followers.")
def add_followers(self):
- email_from = self.env['mail.message']._get_default_from()
+ if not self.env.user.email:
+ raise UserError(_("Unable to post message, please configure the sender's email address."))
+ email_from = self.env.user.email_formatted
for wizard in self:
Model = self.env[wizard.res_model]
document = Model.browse(wizard.res_id)
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index 193f7faf38b..d3b0e6f16bf 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -54,14 +54,14 @@ class MailComposer(models.TransientModel):
result = super(MailComposer, self).default_get(fields)
# author
- if 'author_id' not in result:
- result['author_id'] = self.env.user.partner_id.id
- if 'email_from' not in result and self.env.user.email:
- result['email_from'] = self.env.user.email_formatted
- elif 'email_from' not in result:
- author = self.env['res.partner'].browse(result['author_id'])
- if author.email:
- result['email_from'] = tools.formataddr((author.name, author.email))
+ missing_author = 'author_id' in fields and 'author_id' not in result
+ missing_email_from = 'email_from' in fields and 'email_from' not in result
+ if missing_author or missing_email_from:
+ author_id, email_from = self.env['mail.thread']._message_compute_author(result.get('author_id'), result.get('email_from'), raise_exception=False)
+ if missing_email_from:
+ result['email_from'] = email_from
+ if missing_author:
+ result['author_id'] = author_id
result['composition_mode'] = result.get('composition_mode', 'comment')
result['model'] = result.get('model', self._context.get('active_model'))
diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py
index cd098b3a347..999b3f824b0 100644
--- a/addons/mass_mailing/models/mailing.py
+++ b/addons/mass_mailing/models/mailing.py
@@ -69,7 +69,7 @@ class MassMailing(models.Model):
active = fields.Boolean(default=True, tracking=True)
subject = fields.Char('Subject', help='Subject of emails to send', required=True, translate=True)
email_from = fields.Char(string='Send From', required=True,
- default=lambda self: self.env['mail.message']._get_default_from())
+ default=lambda self: self.env.user.email_formatted)
sent_date = fields.Datetime(string='Sent Date', copy=False)
schedule_date = fields.Datetime(string='Scheduled for', tracking=True)
# don't translate 'body_arch', the translations are only on 'body_html'
@@ -92,7 +92,7 @@ class MassMailing(models.Model):
reply_to_mode = fields.Selection(
[('thread', 'Recipient Followers'), ('email', 'Specified Email Address')], string='Reply-To Mode', required=True)
reply_to = fields.Char(string='Reply To', help='Preferred Reply-To Address',
- default=lambda self: self.env['mail.message']._get_default_from())
+ default=lambda self: self.env.user.email_formatted)
# recipients
mailing_model_real = fields.Char(compute='_compute_model', string='Recipients Real Model', default='mailing.contact', required=True)
mailing_model_id = fields.Many2one('ir.model', string='Recipients Model', domain=[('model', 'in', MASS_MAILING_BUSINESS_MODELS)],
diff --git a/addons/mass_mailing/wizard/mailing_mailing_test.py b/addons/mass_mailing/wizard/mailing_mailing_test.py
index 54424262563..5aa09a71999 100644
--- a/addons/mass_mailing/wizard/mailing_mailing_test.py
+++ b/addons/mass_mailing/wizard/mailing_mailing_test.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import api, fields, models, tools
+from odoo import fields, models, tools
class TestMassMailing(models.TransientModel):
@@ -9,7 +9,7 @@ class TestMassMailing(models.TransientModel):
_description = 'Sample Mail Wizard'
email_to = fields.Char(string='Recipients', required=True,
- help='Comma-separated list of email addresses.', default=lambda self: self.env['mail.message']._get_default_from())
+ help='Comma-separated list of email addresses.', default=lambda self: self.env.user.email_formatted)
mass_mailing_id = fields.Many2one('mailing.mailing', string='Mailing', required=True, ondelete='cascade')
def send_mail_test(self):
diff --git a/addons/payment_paypal/models/payment.py b/addons/payment_paypal/models/payment.py
index 64afaf6ad7c..40b38c70092 100644
--- a/addons/payment_paypal/models/payment.py
+++ b/addons/payment_paypal/models/payment.py
@@ -199,7 +199,8 @@ class TxPaypal(models.Model):
'body_html': mail_body,
'subject': _('Add your Paypal account to Odoo'),
'email_to': self.acquirer_id.paypal_email_account,
- 'email_from': self.acquirer_id.create_uid.email
+ 'email_from': self.acquirer_id.create_uid.email_formatted,
+ 'author_id': self.acquirer_id.create_uid.partner_id.id,
}
self.env['mail.mail'].sudo().create(mail_values).send()
diff --git a/addons/website_slides/models/slide_slide.py b/addons/website_slides/models/slide_slide.py
index 0a61291bb2c..9e0730a1abb 100644
--- a/addons/website_slides/models/slide_slide.py
+++ b/addons/website_slides/models/slide_slide.py
@@ -502,10 +502,18 @@ class Slide(models.Model):
mail_ids = []
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
for record in self:
+ template = self.channel_id.share_template_id.with_context(
+ user=self.env.user,
+ email=email,
+ base_url=base_url,
+ fullscreen=fullscreen
+ )
+ email_values = {'email_to': email}
if self.env.user.has_group('base.group_portal'):
- mail_ids.append(self.channel_id.share_template_id.with_context(user=self.env.user, email=email, base_url=base_url, fullscreen=fullscreen).sudo().send_mail(record.id, notif_layout='mail.mail_notification_light', email_values={'email_from': self.env.company.catchall_formatted or self.env.company.email, 'email_to': email}))
- else:
- mail_ids.append(self.channel_id.share_template_id.with_context(user=self.env.user, email=email, base_url=base_url, fullscreen=fullscreen).send_mail(record.id, notif_layout='mail.mail_notification_light', email_values={'email_to': email}))
+ template = template.sudo()
+ email_values['email_from'] = self.env.company.catchall_formatted or self.env.company.email_formatted
+
+ mail_ids.append(template.send_mail(record.id, notif_layout='mail.mail_notification_light', email_values=email_values))
return mail_ids
def action_like(self):