diff --git a/addons/mail/controllers/discuss.py b/addons/mail/controllers/discuss.py index b9a7454d963..4cfcaacce57 100644 --- a/addons/mail/controllers/discuss.py +++ b/addons/mail/controllers/discuss.py @@ -118,6 +118,6 @@ class DiscussController(http.Controller): try: records.check_access_rule('read') records.check_access_rights('read') - except: + except Exception: return {} return records._message_get_suggested_recipients() diff --git a/addons/mail/models/ir_attachment.py b/addons/mail/models/ir_attachment.py index 7fe59ec8ee4..6cf408a8a26 100644 --- a/addons/mail/models/ir_attachment.py +++ b/addons/mail/models/ir_attachment.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, models +from odoo import models from odoo.exceptions import AccessError from odoo.http import request diff --git a/addons/mail/models/mail_activity.py b/addons/mail/models/mail_activity.py index d4eae30fe39..a976bb318cc 100644 --- a/addons/mail/models/mail_activity.py +++ b/addons/mail/models/mail_activity.py @@ -125,7 +125,7 @@ class MailActivity(models.Model): today_default = date.today() today = today_default if tz: - today_utc = pytz.UTC.localize(datetime.utcnow()) + today_utc = pytz.utc.localize(datetime.utcnow()) today_tz = today_utc.astimezone(pytz.timezone(tz)) today = date(year=today_tz.year, month=today_tz.month, day=today_tz.day) diff = (date_deadline - today) diff --git a/addons/mail/models/mail_activity_mixin.py b/addons/mail/models/mail_activity_mixin.py index 3412147c834..b7334dc7c11 100644 --- a/addons/mail/models/mail_activity_mixin.py +++ b/addons/mail/models/mail_activity_mixin.py @@ -187,7 +187,7 @@ class MailActivityMixin(models.AbstractModel): self._cr.execute( query, { - 'today_utc': pytz.UTC.localize(datetime.utcnow()), + 'today_utc': pytz.utc.localize(datetime.utcnow()), 'res_model_table': self._name, 'search_states_int': list(search_states_int) }, diff --git a/addons/mail/models/mail_activity_type.py b/addons/mail/models/mail_activity_type.py index da89c9b8e27..c305919c804 100644 --- a/addons/mail/models/mail_activity_type.py +++ b/addons/mail/models/mail_activity_type.py @@ -21,7 +21,6 @@ class MailActivityType(models.Model): ['&', ('is_mail_thread', '=', True), ('transient', '=', False)]) ] - name = fields.Char('Name', required=True, translate=True) summary = fields.Char('Default Summary', translate=True) sequence = fields.Integer('Sequence', default=10) diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py index 64a56c4403d..b36ad00001a 100644 --- a/addons/mail/models/mail_mail.py +++ b/addons/mail/models/mail_mail.py @@ -28,6 +28,14 @@ class MailMail(models.Model): _order = 'id desc' _rec_name = 'subject' + @api.model + def default_get(self, fields): + # protection for `default_type` values leaking from menu action context (e.g. for invoices) + # To remove when automatic context propagation is removed in web client + if self._context.get('default_type') not in type(self).message_type.base_field.selection: + self = self.with_context(dict(self._context, default_type=None)) + return super(MailMail, self).default_get(fields) + # content mail_message_id = fields.Many2one('mail.message', 'Message', required=True, ondelete='cascade', index=True, auto_join=True) body_html = fields.Text('Rich-text Contents', help="Rich-text/HTML message") @@ -104,14 +112,6 @@ class MailMail(models.Model): self.env['mail.message'].browse(mail_msg_cascade_ids).unlink() return res - @api.model - def default_get(self, fields): - # protection for `default_type` values leaking from menu action context (e.g. for invoices) - # To remove when automatic context propagation is removed in web client - if self._context.get('default_type') not in type(self).message_type.base_field.selection: - self = self.with_context(dict(self._context, default_type=None)) - return super(MailMail, self).default_get(fields) - def action_retry(self): self.filtered(lambda mail: mail.state == 'exception').mark_outgoing() diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index 0f6cc22c419..da8fd308120 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -133,7 +133,7 @@ class Message(models.Model): 'res.partner', 'mail_notification', string='Partners with Need Action', context={'active_test': False}, depends=['notification_ids']) needaction = fields.Boolean( - 'Need Action', compute='_get_needaction', search='_search_needaction', + 'Need Action', compute='_compute_needaction', search='_search_needaction', help='Need Action') has_error = fields.Boolean( 'Has error', compute='_compute_has_error', search='_search_has_error', @@ -146,7 +146,7 @@ class Message(models.Model): starred_partner_ids = fields.Many2many( 'res.partner', 'mail_message_res_partner_starred_rel', string='Favorited By') starred = fields.Boolean( - 'Starred', compute='_get_starred', search='_search_starred', compute_sudo=False, + 'Starred', compute='_compute_starred', search='_search_starred', compute_sudo=False, help='Current user has a starred notification linked to this message') # tracking tracking_value_ids = fields.One2many( @@ -183,7 +183,7 @@ class Message(models.Model): plaintext_ct = '' if not message.body else tools.html2plaintext(message.body) message.description = plaintext_ct[:30] + '%s' % (' [...]' if len(plaintext_ct) >= 30 else '') - def _get_needaction(self): + def _compute_needaction(self): """ Need action on a mail.message = notified on my channel """ my_messages = self.env['mail.notification'].sudo().search([ ('mail_message_id', 'in', self.ids), @@ -212,7 +212,7 @@ class Message(models.Model): @api.depends('starred_partner_ids') @api.depends_context('uid') - def _get_starred(self): + def _compute_starred(self): """ Compute if the message is starred by the current user. """ # TDE FIXME: use SQL starred = self.sudo().filtered(lambda msg: self.env.user.partner_id in msg.starred_partner_ids) diff --git a/addons/mail/models/mail_notification.py b/addons/mail/models/mail_notification.py index 6626432b3cf..1ee834b21a9 100644 --- a/addons/mail/models/mail_notification.py +++ b/addons/mail/models/mail_notification.py @@ -50,6 +50,10 @@ class MailNotification(models.Model): 'Customer is required for inbox / email notification'), ] + # ------------------------------------------------------------ + # CRUD + # ------------------------------------------------------------ + def init(self): self._cr.execute(""" CREATE INDEX IF NOT EXISTS mail_notification_res_partner_id_is_read_notification_status_mail_message_id @@ -73,13 +77,6 @@ class MailNotification(models.Model): vals['read_date'] = fields.Datetime.now() return super(MailNotification, self).write(vals) - def format_failure_reason(self): - self.ensure_one() - if self.failure_type != 'unknown': - return dict(type(self).failure_type.selection).get(self.failure_type, _('No Error')) - else: - return _("Unknown error") + ": %s" % (self.failure_reason or '') - @api.model def _gc_notifications(self, max_age_days=180): domain = [ @@ -90,6 +87,21 @@ class MailNotification(models.Model): ] return self.search(domain).unlink() + # ------------------------------------------------------------ + # TOOLS + # ------------------------------------------------------------ + + def format_failure_reason(self): + self.ensure_one() + if self.failure_type != 'unknown': + return dict(type(self).failure_type.selection).get(self.failure_type, _('No Error')) + else: + return _("Unknown error") + ": %s" % (self.failure_reason or '') + + # ------------------------------------------------------------ + # DISCUSS + # ------------------------------------------------------------ + def _filtered_for_web_client(self): """Returns only the notifications to show on the web client.""" return self.filtered(lambda n: diff --git a/addons/mail/models/res_config_settings.py b/addons/mail/models/res_config_settings.py index 6cae4f3b0d5..f2272520160 100644 --- a/addons/mail/models/res_config_settings.py +++ b/addons/mail/models/res_config_settings.py @@ -3,8 +3,6 @@ import datetime -from werkzeug import urls - from odoo import api, fields, models, tools diff --git a/addons/mail/models/res_partner.py b/addons/mail/models/res_partner.py index f7f043cad3c..e9bae2de172 100644 --- a/addons/mail/models/res_partner.py +++ b/addons/mail/models/res_partner.py @@ -1,15 +1,11 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import logging - from odoo import _, api, fields, models, tools from odoo.addons.bus.models.bus_presence import AWAY_TIMER from odoo.addons.bus.models.bus_presence import DISCONNECTION_TIMER from odoo.osv import expression -_logger = logging.getLogger(__name__) - class Partner(models.Model): """ Update partner to add a field about notification preferences. Add a generic opt-out field that can be used @@ -18,12 +14,12 @@ class Partner(models.Model): _inherit = ['res.partner', 'mail.activity.mixin', 'mail.thread.blacklist'] _mail_flat_thread = False + # override to add tracking email = fields.Char(tracking=1) phone = fields.Char(tracking=2) - - channel_ids = fields.Many2many('mail.channel', 'mail_channel_partner', 'partner_id', 'channel_id', string='Channels', copy=False) - # override the field to track the visibility of user user_id = fields.Many2one(tracking=True) + # channels + channel_ids = fields.Many2many('mail.channel', 'mail_channel_partner', 'partner_id', 'channel_id', string='Channels', copy=False) def _compute_im_status(self): super()._compute_im_status() @@ -32,6 +28,31 @@ class Partner(models.Model): if odoobot in self: odoobot.im_status = 'bot' + # pseudo computes + + def _get_needaction_count(self): + """ compute the number of needaction of the current partner """ + self.ensure_one() + self.env['mail.notification'].flush(['is_read', 'res_partner_id']) + self.env.cr.execute(""" + SELECT count(*) as needaction_count + FROM mail_notification R + WHERE R.res_partner_id = %s AND (R.is_read = false OR R.is_read IS NULL)""", (self.id,)) + return self.env.cr.dictfetchall()[0].get('needaction_count') + + def _get_starred_count(self): + """ compute the number of starred of the current partner """ + self.ensure_one() + self.env.cr.execute(""" + SELECT count(*) as starred_count + FROM mail_message_res_partner_starred_rel R + WHERE R.res_partner_id = %s """, (self.id,)) + return self.env.cr.dictfetchall()[0].get('starred_count') + + # ------------------------------------------------------------ + # MESSAGING + # ------------------------------------------------------------ + def _message_get_suggested_recipients(self): recipients = super(Partner, self)._message_get_suggested_recipients() for partner in self: @@ -39,11 +60,18 @@ class Partner(models.Model): return recipients def _message_get_default_recipients(self): - return {r.id: { - 'partner_ids': [r.id], - 'email_to': False, - 'email_cc': False} - for r in self} + return { + r.id: + {'partner_ids': [r.id], + 'email_to': False, + 'email_cc': False + } + for r in self + } + + # ------------------------------------------------------------ + # ORM + # ------------------------------------------------------------ @api.model @api.returns('self', lambda value: value.id) @@ -69,6 +97,10 @@ class Partner(models.Model): create_values['email'] = parsed_email return self.create(create_values) + # ------------------------------------------------------------ + # DISCUSS + # ------------------------------------------------------------ + def mail_partner_format(self): partners_format = dict() for partner in self: @@ -86,25 +118,6 @@ class Partner(models.Model): } return partners_format - def _get_needaction_count(self): - """ compute the number of needaction of the current partner """ - self.ensure_one() - self.env['mail.notification'].flush(['is_read', 'res_partner_id']) - self.env.cr.execute(""" - SELECT count(*) as needaction_count - FROM mail_notification R - WHERE R.res_partner_id = %s AND (R.is_read = false OR R.is_read IS NULL)""", (self.id,)) - return self.env.cr.dictfetchall()[0].get('needaction_count') - - def _get_starred_count(self): - """ compute the number of starred of the current partner """ - self.ensure_one() - self.env.cr.execute(""" - SELECT count(*) as starred_count - FROM mail_message_res_partner_starred_rel R - WHERE R.res_partner_id = %s """, (self.id,)) - return self.env.cr.dictfetchall()[0].get('starred_count') - def _message_fetch_failed(self): """Returns all messages, sent by the current partner, that have errors, in the format expected by the web client.""" diff --git a/addons/mail/models/res_users.py b/addons/mail/models/res_users.py index 460d101cc2f..d91cf339801 100644 --- a/addons/mail/models/res_users.py +++ b/addons/mail/models/res_users.py @@ -26,6 +26,10 @@ class Users(models.Model): "- Handle by Emails: notifications are sent to your email address\n" "- Handle in Odoo: notifications appear in your Odoo Inbox") + # ------------------------------------------------------------ + # CRUD + # ------------------------------------------------------------ + @property def SELF_READABLE_FIELDS(self): return super().SELF_READABLE_FIELDS + ['notification_type'] @@ -80,6 +84,10 @@ class Users(models.Model): lambda cp: cp.channel_id.public != 'public' and cp.channel_id.channel_type == 'channel' ).unlink() + # ------------------------------------------------------------ + # DISCUSS + # ------------------------------------------------------------ + def _init_messaging(self): self.ensure_one() partner_root = self.env.ref('base.partner_root')