diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index b3b9124c677..98f25ab908a 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -213,7 +213,7 @@ class Attendee(models.Model): mail_ids.append(invitation_template.send_mail(attendee.id, email_values=email_values, notif_layout='mail.mail_notification_light')) if force_send and mail_ids: - res = self.env['mail.mail'].browse(mail_ids).send() + res = self.env['mail.mail'].sudo().browse(mail_ids).send() return res diff --git a/addons/calendar/tests/test_calendar.py b/addons/calendar/tests/test_calendar.py index 0624281ed0c..8ac53b7889d 100644 --- a/addons/calendar/tests/test_calendar.py +++ b/addons/calendar/tests/test_calendar.py @@ -398,7 +398,7 @@ class TestCalendar(SavepointCaseWithUserDemo): def _test_one_mail_per_attendee(self, m, partners): # check that every attendee receive a (single) mail for the event for partner in partners: - mail = self.env['mail.mail'].search([ + mail = self.env['mail.mail'].sudo().search([ ('recipient_ids', 'in', partner.id), ('subject', 'like', m.name), ]) diff --git a/addons/event/tests/test_mail_schedule.py b/addons/event/tests/test_mail_schedule.py index 7dcc446dc9c..678342c74fa 100644 --- a/addons/event/tests/test_mail_schedule.py +++ b/addons/event/tests/test_mail_schedule.py @@ -56,7 +56,7 @@ class TestMailSchedule(TestEventCommon): # verify that subscription scheduler was auto-executed after each registration self.assertEqual(len(schedulers[0].mail_registration_ids), 2, 'event: incorrect number of mail scheduled date') - mails = self.env['mail.mail'].search([('subject', 'ilike', 'registration'), ('date', '>=', now)], order='date DESC', limit=3) + mails = self.env['mail.mail'].sudo().search([('subject', 'ilike', 'registration'), ('date', '>=', now)], order='date DESC', limit=3) self.assertEqual(len(mails), 2, 'event: wrong number of registration mail sent') for registration in schedulers[0].mail_registration_ids: @@ -73,5 +73,5 @@ class TestMailSchedule(TestEventCommon): self.assertTrue(schedulers[0].mail_sent, 'event: reminder scheduler should have sent an email') self.assertTrue(schedulers[0].done, 'event: reminder scheduler should be done') - mails = self.env['mail.mail'].search([('subject', 'ilike', 'TestEventMail'), ('date', '>=', now)], order='date DESC', limit=3) + mails = self.env['mail.mail'].sudo().search([('subject', 'ilike', 'TestEventMail'), ('date', '>=', now)], order='date DESC', limit=3) self.assertEqual(len(mails), 3, 'event: wrong number of reminders in outgoing mail queue') diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 259cb7e0167..5d505bbc4cc 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -607,6 +607,7 @@ class HrExpense(models.Model): expense_template = self.env.ref(mail_template_id) rendered_body = expense_template.render({'expense': expense}, engine='ir.qweb') body = self.env['mail.thread']._replace_local_links(rendered_body) + # TDE TODO: seems louche, check to use notify if expense.employee_id.user_id.partner_id: expense.message_post( partner_ids=expense.employee_id.user_id.partner_id.ids, @@ -616,7 +617,7 @@ class HrExpense(models.Model): email_layout_xmlid='mail.mail_notification_light', ) else: - self.env['mail.mail'].create({ + self.env['mail.mail'].sudo().create({ 'email_from': self.env.user.email_formatted, 'author_id': self.env.user.partner_id.id, 'body_html': body, diff --git a/addons/im_livechat/controllers/main.py b/addons/im_livechat/controllers/main.py index a4b5c7f8f9d..84f8a83ac47 100644 --- a/addons/im_livechat/controllers/main.py +++ b/addons/im_livechat/controllers/main.py @@ -152,6 +152,8 @@ class LivechatController(http.Controller): @http.route('/im_livechat/email_livechat_transcript', type='json', auth='public', cors="*") def email_livechat_transcript(self, uuid, email): - channel = request.env['mail.channel'].sudo().search([('uuid', '=', uuid)], limit=1) + channel = request.env['mail.channel'].sudo().search([ + ('channel_type', '=', 'livechat'), + ('uuid', '=', uuid)], limit=1) if channel: channel._email_livechat_transcript(email) diff --git a/addons/im_livechat/models/mail_channel.py b/addons/im_livechat/models/mail_channel.py index b5397e38bdf..f7276374e13 100644 --- a/addons/im_livechat/models/mail_channel.py +++ b/addons/im_livechat/models/mail_channel.py @@ -171,7 +171,7 @@ class MailChannel(models.Model): template = self.env.ref('im_livechat.livechat_email_template') mail_body = template.render(render_context, engine='ir.qweb', minimal_qcontext=True) mail_body = self.env['mail.thread']._replace_local_links(mail_body) - mail = self.env['mail.mail'].create({ + mail = self.env['mail.mail'].sudo().create({ 'subject': _('Conversation with %s') % self.livechat_operator_id.name, 'email_from': company.catchall_formatted or company.email_formatted, 'author_id': self.env.user.partner_id.id, diff --git a/addons/l10n_it_edi/models/account_invoice.py b/addons/l10n_it_edi/models/account_invoice.py index bb480d53cb0..6a202f0be09 100644 --- a/addons/l10n_it_edi/models/account_invoice.py +++ b/addons/l10n_it_edi/models/account_invoice.py @@ -278,7 +278,7 @@ class AccountMove(models.Model): 'attachment_ids': [(6, 0, self.l10n_it_einvoice_id.ids)], }) - mail_fattura = self.env['mail.mail'].with_context(wo_return_path=True).create({ + mail_fattura = self.env['mail.mail'].sudo().with_context(wo_return_path=True).create({ 'mail_message_id': message.id, 'email_to': self.env.company.l10n_it_address_recipient_fatturapa, }) diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index ad1a1ef42b4..2d3c54a9591 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -367,7 +367,7 @@ class Channel(models.Model): # Notifies the message author when his message is pending moderation if required on channel. # 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({ + self.env['mail.mail'].sudo().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, @@ -426,7 +426,7 @@ class Channel(models.Model): 'subject': _("Guidelines of channel %s") % self.name, 'recipient_ids': [(4, partner.id)] } - mail = self.env['mail.mail'].create(create_values) + mail = self.env['mail.mail'].sudo().create(create_values) return True def _update_moderation_email(self, emails, status): diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index 5be02450f87..0a038e381eb 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -134,7 +134,7 @@ class Message(models.Model): # By setting up the inverse one2many, we avoid to have to do a search to find the mails linked to the `mail.message` # as the cache value for this inverse one2many is up-to-date. # Besides for new messages, and messages never sending emails, there was no mail, and it was searching for nothing. - mail_ids = fields.One2many('mail.mail', 'mail_message_id', string='Mails') + mail_ids = fields.One2many('mail.mail', 'mail_message_id', string='Mails', groups="base.group_system") canned_response_ids = fields.One2many('mail.shortcode', 'message_ids', string="Canned Responses", store=False) def _get_needaction(self): diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py index 2a0261623a2..ff161dc07a1 100644 --- a/addons/mail/models/mail_template.py +++ b/addons/mail/models/mail_template.py @@ -405,6 +405,11 @@ class MailTemplate(models.Model): # EMAIL # ---------------------------------------- + def _send_check_access(self, res_ids): + records = self.env[self.model].browse(res_ids) + records.check_access_rights('read') + records.check_access_rule('read') + def send_mail(self, res_id, force_send=False, raise_exception=False, email_values=None, notif_layout=False): """ Generates a new mail.mail. Template is rendered on record given by res_id and model coming from template. @@ -417,8 +422,11 @@ class MailTemplate(models.Model): :param str notif_layout: optional notification layout to encapsulate the generated email; :returns: id of the mail.mail that was created """ + + # Grant access to send_mail only if access to related document self.ensure_one() - Mail = self.env['mail.mail'] + self._send_check_access([res_id]) + Attachment = self.env['ir.attachment'] # TDE FIXME: should remove default_type from context # create a mail_mail based on values, without attachments @@ -447,7 +455,7 @@ class MailTemplate(models.Model): } body = template.render(template_ctx, engine='ir.qweb', minimal_qcontext=True) values['body_html'] = self.env['mail.thread']._replace_local_links(body) - mail = Mail.create(values) + mail = self.env['mail.mail'].sudo().create(values) # manage attachments for attachment in attachments: diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index d0abc8adab7..f3f703666b3 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -697,7 +697,7 @@ class MailThread(models.AbstractModel): 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() + self.env['mail.mail'].sudo().create(bounce_mail_values).send() @api.model def _routing_handle_bounce(self, email_message, message_dict): diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv index 42656580435..eb52637ff2e 100644 --- a/addons/mail/security/ir.model.access.csv +++ b/addons/mail/security/ir.model.access.csv @@ -4,7 +4,7 @@ access_mail_message_portal,mail.message.portal,model_mail_message,base.group_por access_mail_message_user,mail.message.user,model_mail_message,base.group_user,1,1,1,1 access_mail_mail_all,mail.mail.all,model_mail_mail,,0,0,0,0 access_mail_mail_portal,mail.mail.portal,model_mail_mail,base.group_portal,0,0,0,0 -access_mail_mail_user,mail.mail.user,model_mail_mail,base.group_user,1,1,1,0 +access_mail_mail_user,mail.mail.user,model_mail_mail,base.group_user,0,0,0,0 access_mail_mail_system,mail.mail.system,model_mail_mail,base.group_system,1,1,1,1 access_mail_followers_all,mail.followers.all,model_mail_followers,,1,0,0,0 access_mail_followers_portal,mail.followers.portal,model_mail_followers,base.group_portal,1,1,1,0 @@ -28,7 +28,6 @@ access_mail_tracking_value_all,mail.tracking.value.all,model_mail_tracking_value access_mail_tracking_value_portal,mail.tracking.value.portal,model_mail_tracking_value,base.group_portal,0,0,0,0 access_mail_tracking_value_user,mail.tracking.value.user,model_mail_tracking_value,base.group_user,0,0,0,0 access_mail_tracking_value_system,mail.tracking.value.system,model_mail_tracking_value,base.group_system,1,1,1,1 -access_mail_thread_all,mail.thread.all,model_mail_thread,,1,1,1,1 access_publisher_warranty_contract_all,publisher.warranty.contract.all,model_publisher_warranty_contract,,1,1,1,1 access_mail_template,mail.template,model_mail_template,base.group_user,1,1,1,0 access_mail_template_system,mail.template_system,model_mail_template,base.group_system,1,1,1,1 diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index d3b0e6f16bf..fe4660e451b 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -208,7 +208,6 @@ class MailComposer(models.TransientModel): # Mass Mailing mass_mode = wizard.composition_mode in ('mass_mail', 'mass_post') - Mail = self.env['mail.mail'] ActiveModel = self.env[wizard.model] if wizard.model and hasattr(self.env[wizard.model], 'message_post') else self.env['mail.thread'] if wizard.composition_mode == 'mass_post': # do not send emails directly but use the queue instead @@ -233,11 +232,15 @@ class MailComposer(models.TransientModel): subtype_id = self.env['ir.model.data'].xmlid_to_res_id('mail.mt_comment') for res_ids in sliced_res_ids: - batch_mails = Mail + # mass mail mode: mail are sudo-ed, as when going through get_mail_values + # standard access rights on related records will be checked when browsing them + # to compute mail values. If people have access to the records they have rights + # to create lots of emails in sudo as it is consdiered as a technical model. + batch_mails_sudo = self.env['mail.mail'].sudo() all_mail_values = wizard.get_mail_values(res_ids) for res_id, mail_values in all_mail_values.items(): if wizard.composition_mode == 'mass_mail': - batch_mails |= Mail.create(mail_values) + batch_mails_sudo |= self.env['mail.mail'].sudo().create(mail_values) else: post_params = dict( message_type=wizard.message_type, @@ -258,7 +261,7 @@ class MailComposer(models.TransientModel): ActiveModel.browse(res_id).message_post(**post_params) if wizard.composition_mode == 'mass_mail': - batch_mails.send(auto_commit=auto_commit) + batch_mails_sudo.send(auto_commit=auto_commit) def get_mail_values(self, res_ids): """Generate the values that will be used by send_mail to create mail_messages diff --git a/addons/mass_mailing/models/mail_mail.py b/addons/mass_mailing/models/mail_mail.py index a0f810c5c7b..749c6dc6133 100644 --- a/addons/mass_mailing/models/mail_mail.py +++ b/addons/mass_mailing/models/mail_mail.py @@ -23,8 +23,7 @@ class MailMail(models.Model): mails = super(MailMail, self).create(values_list) for mail, values in zip(mails, values_list): if values.get('mailing_trace_ids'): - mail_sudo = mail.sudo() - mail_sudo.mailing_trace_ids.write({'message_id': mail_sudo.message_id, 'state': 'outgoing'}) + mail.mailing_trace_ids.write({'message_id': mail.message_id, 'state': 'outgoing'}) return mails def _get_tracking_url(self): diff --git a/addons/mass_mailing/tests/test_mass_mailing_shortener.py b/addons/mass_mailing/tests/test_mass_mailing_shortener.py index 8bd247691ea..681bbbf0062 100644 --- a/addons/mass_mailing/tests/test_mass_mailing_shortener.py +++ b/addons/mass_mailing/tests/test_mass_mailing_shortener.py @@ -68,7 +68,7 @@ Email: test@odoo.com mass_mailing.action_put_in_queue() mass_mailing._process_mass_mailing_queue() - sent_mails = self.env['mail.mail'].search([('mailing_id', '=', mass_mailing.id)]) + sent_mails = self.env['mail.mail'].sudo().search([('mailing_id', '=', mass_mailing.id)]) sent_messages = sent_mails.mapped('mail_message_id') self.assertEqual(len(mailing_list_A.contact_ids), len(sent_messages), diff --git a/addons/mass_mailing/wizard/mailing_mailing_test.py b/addons/mass_mailing/wizard/mailing_mailing_test.py index 5aa09a71999..5a107a8a132 100644 --- a/addons/mass_mailing/wizard/mailing_mailing_test.py +++ b/addons/mass_mailing/wizard/mailing_mailing_test.py @@ -14,7 +14,7 @@ class TestMassMailing(models.TransientModel): def send_mail_test(self): self.ensure_one() - mails = self.env['mail.mail'] + mails_sudo = self.env['mail.mail'].sudo() mailing = self.mass_mailing_id test_emails = tools.email_split(self.email_to) mass_mail_layout = self.env.ref('mass_mailing.mass_mailing_mail_layout') @@ -33,7 +33,7 @@ class TestMassMailing(models.TransientModel): 'attachment_ids': [(4, attachment.id) for attachment in mailing.attachment_ids], 'auto_delete': True, } - mail = self.env['mail.mail'].create(mail_values) - mails |= mail - mails.send() + mail = self.env['mail.mail'].sudo().create(mail_values) + mails_sudo |= mail + mails_sudo.send() return True diff --git a/addons/survey/tests/test_certification_flow.py b/addons/survey/tests/test_certification_flow.py index f9a9010b68e..4625fff5d5e 100644 --- a/addons/survey/tests/test_certification_flow.py +++ b/addons/survey/tests/test_certification_flow.py @@ -133,7 +133,7 @@ class TestCertificationFlow(common.TestSurveyCommon, HttpCase): self.assertNotIn("I think they're great!", user_inputs.mapped('user_input_line_ids.value_free_text')) self.assertIn("Just kidding, I don't like it...", user_inputs.mapped('user_input_line_ids.value_free_text')) - certification_email = self.env['mail.mail'].search([], limit=1, order="create_date desc") + certification_email = self.env['mail.mail'].sudo().search([], limit=1, order="create_date desc") # Check certification email correctly sent and contains document self.assertIn("User Certification for SO lines", certification_email.subject) self.assertIn("employee@example.com", certification_email.email_to) diff --git a/addons/test_mail/tests/test_ir_actions.py b/addons/test_mail/tests/test_ir_actions.py index 6a547081a15..d67124b2153 100644 --- a/addons/test_mail/tests/test_ir_actions.py +++ b/addons/test_mail/tests/test_ir_actions.py @@ -12,7 +12,7 @@ class TestServerActionsEmail(TestMailCommon, TestServerActionsBase): self.action.write({'state': 'email', 'template_id': email_template.id}) self.action.with_context(self.context).run() # check an email is waiting for sending - mail = self.env['mail.mail'].search([('subject', '=', 'About TestingPartner')]) + mail = self.env['mail.mail'].sudo().search([('subject', '=', 'About TestingPartner')]) self.assertEqual(len(mail), 1) # check email content self.assertEqual(mail.body, '

Hello TestingPartner

') diff --git a/addons/test_mail/tests/test_mail_mail.py b/addons/test_mail/tests/test_mail_mail.py index 7fb29598e6f..7e6b0f98ba2 100644 --- a/addons/test_mail/tests/test_mail_mail.py +++ b/addons/test_mail/tests/test_mail_mail.py @@ -15,8 +15,7 @@ class TestMailMail(TestMailCommon): @mute_logger('odoo.addons.mail.models.mail_mail') def test_mail_message_notify_from_mail_mail(self): # Due ot post-commit hooks, store send emails in every step - # self.email_to_list = [] - mail = self.env['mail.mail'].create({ + mail = self.env['mail.mail'].sudo().create({ 'body_html': '

Test

', 'email_to': 'test@example.com', 'partner_ids': [(4, self.user_employee.partner_id.id)] @@ -28,7 +27,7 @@ class TestMailMail(TestMailCommon): @mute_logger('odoo.addons.mail.models.mail_mail') def test_mail_message_values_unicode(self): - mail = self.env['mail.mail'].create({ + mail = self.env['mail.mail'].sudo().create({ 'body_html': '

Test

', 'email_to': 'test.😊@example.com', 'partner_ids': [(4, self.user_employee.partner_id.id)] @@ -45,7 +44,7 @@ class TestMailMailRace(common.TransactionCase): 'name': 'Ernest Partner', }) # we need to simulate a mail sent by the cron task, first create mail, message and notification by hand - mail = self.env['mail.mail'].create({ + mail = self.env['mail.mail'].sudo().create({ 'body_html': '

Test

', 'notification': True, 'state': 'outgoing', diff --git a/addons/test_mail/tests/test_mail_template.py b/addons/test_mail/tests/test_mail_template.py index a6634ac34f8..a845ffc0a79 100644 --- a/addons/test_mail/tests/test_mail_template.py +++ b/addons/test_mail/tests/test_mail_template.py @@ -48,7 +48,7 @@ class TestMailTemplate(TestMailCommon, TestRecipients): @mute_logger('odoo.addons.mail.models.mail_mail') def test_template_send_email(self): mail_id = self.email_template.send_mail(self.test_record.id) - mail = self.env['mail.mail'].browse(mail_id) + mail = self.env['mail.mail'].sudo().browse(mail_id) self.assertEqual(mail.subject, 'About %s' % self.test_record.name) self.assertEqual(mail.email_to, self.email_template.email_to) self.assertEqual(mail.email_cc, self.email_template.email_cc) diff --git a/addons/test_mail/tests/test_message_composer.py b/addons/test_mail/tests/test_message_composer.py index 563f4390d9f..e94b5a533c3 100644 --- a/addons/test_mail/tests/test_message_composer.py +++ b/addons/test_mail/tests/test_message_composer.py @@ -72,7 +72,7 @@ class TestComposer(TestMailCommon, TestRecipients): }).send_mail() # check mail_mail - mails = self.env['mail.mail'].search([('subject', 'ilike', 'Testing')]) + mails = self.env['mail.mail'].sudo().search([('subject', 'ilike', 'Testing')]) for mail in mails: self.assertEqual(mail.recipient_ids, self.partner_1 | self.partner_2, 'compose wizard: mail_mail mass mailing: mail.mail in mass mail incorrect recipients') diff --git a/addons/test_mail/tests/test_message_post.py b/addons/test_mail/tests/test_message_post.py index 14a8e9e3628..bf894fafe12 100644 --- a/addons/test_mail/tests/test_message_post.py +++ b/addons/test_mail/tests/test_message_post.py @@ -86,7 +86,7 @@ class TestMessagePost(TestMailCommon, TestRecipients): self.assertEqual(msg.channel_ids, self.env['mail.channel']) # notifications emails should have been deleted - self.assertFalse(self.env['mail.mail'].search([('mail_message_id', '=', msg.id)]), + self.assertFalse(self.env['mail.mail'].sudo().search([('mail_message_id', '=', msg.id)]), 'message_post: mail.mail notifications should have been auto-deleted') @mute_logger('odoo.addons.mail.models.mail_mail') @@ -101,7 +101,7 @@ class TestMessagePost(TestMailCommon, TestRecipients): ) # notifications emails should not have been deleted: one for customers, one for user - self.assertEqual(len(self.env['mail.mail'].search([('mail_message_id', '=', msg.id)])), 2) + self.assertEqual(len(self.env['mail.mail'].sudo().search([('mail_message_id', '=', msg.id)])), 2) @mute_logger('odoo.addons.mail.models.mail_mail') def test_post_notifications_emails_tweak(self): diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py index 00676a580d3..b7948c23a4b 100644 --- a/addons/test_mail/tests/test_performance.py +++ b/addons/test_mail/tests/test_performance.py @@ -459,8 +459,8 @@ class TestMailComplexPerformance(BaseMailPerformance): 'recipient_ids': [(4, pid) for pid in self.partners.ids], }) mail_ids = mail.ids - with self.assertQueryCount(__system__=8, emp=9): - self.env['mail.mail'].browse(mail_ids).send() + with self.assertQueryCount(__system__=8, emp=8): + self.env['mail.mail'].sudo().browse(mail_ids).send() self.assertEqual(mail.body_html, '

Test

') self.assertEqual(mail.reply_to, formataddr(('%s %s' % (self.env.company.name, self.umbrella.name), 'test-alias@example.com')))