[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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -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)],
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user