From f5c6f14bb1feff631d17990989f1569e070526fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 27 Aug 2020 12:52:35 +0000 Subject: [PATCH] [REF] mail: move generic methods from thread to BaseModel PURPOSE Since v13 some commits added code in mail thread file. This file is already quite long and keeping it organized allow to understand its content. SPECIFICATIONS Some methods defined on mail.thread may actually be called on models not inheriting from mail.thread . Instead of having model methods on mail thread receiving a records parameter it seems better to have those available directly on BaseModel. Move static_message_track on BaseModel in models.py. This method is now called _mail_track, to be coherent with othe rmail-related naming. It is used in accounting to track values in line model that does not inherit from mail. thread. Update accounting accordingly (followup of d862965). Move _message_get_default_recipients_on_records in models.py. Renaming it _message_get_default_recipients() allow to be compatible with current behavior and current override available in some addons (like CRM, event, ...). Move _notify_get_reply_to_on_records in models.py. Renaming it _notify_get_reply_to() allows to be shorter and coherent. Move _alias_check_contact_on_record in models.py. Renaming it _alias_check_contact_() allows to be shorter and coherent. Its override in hr is also moved on BaseModel. LINKS Task ID-2327096 (code cleaning) PR #56631 X-original-commit: f5df1ed912455e5ed52a65df3149f30a9d424de0 --- addons/account/models/account_move.py | 10 +- addons/hr/models/__init__.py | 1 + addons/hr/models/mail_alias.py | 21 +-- addons/hr/models/models.py | 20 +++ addons/mail/models/mail_alias_mixin.py | 25 ---- addons/mail/models/mail_channel.py | 4 +- addons/mail/models/mail_message.py | 6 +- addons/mail/models/mail_template.py | 2 +- addons/mail/models/mail_thread.py | 158 +------------------- addons/mail/models/models.py | 165 ++++++++++++++++++++- addons/mail/wizard/mail_compose_message.py | 4 +- 11 files changed, 203 insertions(+), 213 deletions(-) create mode 100644 addons/hr/models/models.py diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 99f6195d4f6..7bfff747fdd 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -3713,16 +3713,16 @@ class AccountMoveLine(models.Model): for move_id, modified_lines in move_initial_values.items(): tmp_move = {move_id: []} for line in self.filtered(lambda l: l.move_id.id == move_id): - tracked_field = self.env['mail.thread'].static_message_track(line, ref_fields, modified_lines) # Return a tuple like (changed field, ORM command) + changes, tracking_value_ids = line._mail_track(ref_fields, modified_lines) # Return a tuple like (changed field, ORM command) tmp = {'line_id': line.id} - if len(tracked_field[1]) > 0: - selected_field = tracked_field[1][0][2] # Get the last element of the tuple in the list of ORM command. (changed, [(0, 0, THIS)]) + if tracking_value_ids: + selected_field = tracking_value_ids[0][2] # Get the last element of the tuple in the list of ORM command. (changed, [(0, 0, THIS)]) tmp.update({ **{'field_name': selected_field.get('field_desc')}, **self._get_formated_values(selected_field) }) - elif len(tracked_field[0]): - field_name = line._fields[tracked_field[0].pop()].string # Get the field name + elif changes: + field_name = line._fields[changes.pop()].string # Get the field name tmp.update({ 'error': True, 'field_error': field_name diff --git a/addons/hr/models/__init__.py b/addons/hr/models/__init__.py index 1c06c535ae8..f9e64bc7013 100644 --- a/addons/hr/models/__init__.py +++ b/addons/hr/models/__init__.py @@ -10,6 +10,7 @@ from . import hr_job from . import hr_plan from . import mail_alias from . import mail_channel +from . import models from . import res_config_settings from . import res_partner from . import res_users diff --git a/addons/hr/models/mail_alias.py b/addons/hr/models/mail_alias.py index 3d652142f68..69042cfb1bd 100644 --- a/addons/hr/models/mail_alias.py +++ b/addons/hr/models/mail_alias.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import fields, models, tools, _ +from odoo import fields, models, _ class Alias(models.Model): @@ -16,21 +16,4 @@ class Alias(models.Model): return _("""Hi,
Your document has not been created because your email address is not recognized.
Please send emails with the email address recorded on your employee information, or contact your HR manager.""") - else: - return super(Alias, self)._get_alias_bounced_body_fallback(message_dict) - - -class AliasMixin(models.AbstractModel): - _inherit = 'mail.alias.mixin' - - def _alias_check_contact_on_record(self, record, message, message_dict, alias): - if alias.alias_contact == 'employees': - email_from = tools.decode_message_header(message, 'From') - email_address = tools.email_split(email_from)[0] - employee = self.env['hr.employee'].search([('work_email', 'ilike', email_address)], limit=1) - if not employee: - employee = self.env['hr.employee'].search([('user_id.email', 'ilike', email_address)], limit=1) - if not employee: - return _('restricted to employees') - return True - return super(AliasMixin, self)._alias_check_contact_on_record(record, message, message_dict, alias) + return super(Alias, self)._get_alias_bounced_body_fallback(message_dict) diff --git a/addons/hr/models/models.py b/addons/hr/models/models.py new file mode 100644 index 00000000000..dd615798364 --- /dev/null +++ b/addons/hr/models/models.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, tools, _ + + +class BaseModel(models.AbstractModel): + _inherit = 'base' + + def _alias_get_error_message(self, message, message_dict, alias): + if alias.alias_contact == 'employees': + email_from = tools.decode_message_header(message, 'From') + email_address = tools.email_split(email_from)[0] + employee = self.env['hr.employee'].search([('work_email', 'ilike', email_address)], limit=1) + if not employee: + employee = self.env['hr.employee'].search([('user_id.email', 'ilike', email_address)], limit=1) + if not employee: + return _('restricted to employees') + return True + return super(BaseModel, self)._alias_get_error_message(message, message_dict, alias) diff --git a/addons/mail/models/mail_alias_mixin.py b/addons/mail/models/mail_alias_mixin.py index f753136669f..12eb8d4406a 100644 --- a/addons/mail/models/mail_alias_mixin.py +++ b/addons/mail/models/mail_alias_mixin.py @@ -120,28 +120,3 @@ class AliasMixin(models.AbstractModel): else: record_values[fname] = values.get(fname) return alias_values, record_values - - # -------------------------------------------------- - # GATEWAY - # -------------------------------------------------- - - def _alias_check_contact(self, message, message_dict, alias): - """ Main mixin method that inheriting models may inherit in order - to implement a specifc behavior. """ - return self._alias_check_contact_on_record(self, message, message_dict, alias) - - def _alias_check_contact_on_record(self, record, message, message_dict, alias): - """ Generic method that takes a record not necessarily inheriting from - mail.alias.mixin. """ - author = self.env['res.partner'].browse(message_dict.get('author_id', False)) - if alias.alias_contact == 'followers': - if not record.ids: - return _('incorrectly configured alias (unknown reference record)') - if not hasattr(record, "message_partner_ids") or not hasattr(record, "message_channel_ids"): - return _('incorrectly configured alias') - accepted_partner_ids = record.message_partner_ids | record.message_channel_ids.mapped('channel_partner_ids') - if not author or author not in accepted_partner_ids: - return _('restricted to followers') - elif alias.alias_contact == 'partners' and not author: - return _('restricted to known authors') - return True diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index 1cbf07475d7..70f0d9eb78c 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -386,13 +386,13 @@ class Channel(models.Model): }) return message - def _alias_check_contact(self, message, message_dict, alias): + def _alias_get_error_message(self, message, message_dict, alias): if alias.alias_contact == 'followers' and self.ids: author = self.env['res.partner'].browse(message_dict.get('author_id', False)) if not author or author not in self.channel_partner_ids: return _('restricted to channel members') return True - return super(Channel, self)._alias_check_contact(message, message_dict, alias) + return super(Channel, self)._alias_get_error_message(message, message_dict, alias) def init(self): self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_channel_partner_seen_message_id_idx',)) diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index f74596c1226..84b0976ed3e 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -1167,15 +1167,15 @@ class Message(models.Model): def _get_reply_to(self, values): """ Return a specific reply_to for the document """ model = values.get('model', self._context.get('default_model')) - res_id = values.get('res_id', self._context.get('default_res_id')) + res_id = values.get('res_id', self._context.get('default_res_id')) or False email_from = values.get('email_from') message_type = values.get('message_type') records = None if self.is_thread_message({'model': model, 'res_id': res_id, 'message_type': message_type}): records = self.env[model].browse([res_id]) else: - res_id = False - return self.env['mail.thread']._notify_get_reply_to_on_records(default=email_from, records=records)[res_id] + records = self.env[model] if model else self.env['mail.thread'] + return records._notify_get_reply_to(default=email_from)[res_id] @api.model def _get_message_id(self, values): diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py index 92783104b65..e0d3c30490e 100644 --- a/addons/mail/models/mail_template.py +++ b/addons/mail/models/mail_template.py @@ -116,7 +116,7 @@ class MailTemplate(models.Model): if self.use_default_to or self._context.get('tpl_force_default_to'): records = self.env[self.model].browse(res_ids).sudo() - default_recipients = self.env['mail.thread']._message_get_default_recipients_on_records(records) + default_recipients = records._message_get_default_recipients() for res_id, recipients in default_recipients.items(): results[res_id].pop('partner_to', None) results[res_id].update(recipients) diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index dacc0ae8b85..9899c2a0692 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -574,38 +574,6 @@ class MailThread(models.AbstractModel): def _track_template(self, changes): return dict() - @api.model - def static_message_track(self, record, tracked_fields, initial): - """ For a given record, fields to check (tuple column name, column info) - and initial values, return a structure that is a tuple containing : - - a set of updated column names - - a list of ORM (0, 0, values) commands to create 'mail.tracking.value' - This static method is usefull when you don't want to inherit from mail.thread but - you want to use '_message_track()' method. - """ - record.ensure_one() - changes = set() # contains onchange tracked fields that changed - tracking_value_ids = [] - - # generate tracked_values data structure: {'col_name': {col_info, new_value, old_value}} - for col_name, col_info in tracked_fields.items(): - if col_name not in initial: - continue - initial_value = initial[col_name] - new_value = record[col_name] - - if new_value != initial_value and (new_value or initial_value): # because browse null != False - tracking_sequence = getattr(record._fields[col_name], 'tracking', - getattr(record._fields[col_name], 'track_sequence', 100)) # backward compatibility with old parameter name - if tracking_sequence is True: - tracking_sequence = 100 - tracking = record.env['mail.tracking.value'].create_tracking_values(initial_value, new_value, col_name, col_info, tracking_sequence, record._name) - if tracking: - tracking_value_ids.append([0, 0, tracking]) - changes.add(col_name) - - return changes, tracking_value_ids - def message_track(self, tracked_fields, initial_values): """ Track updated values. Comparing the initial and current values of the fields given in tracked_fields, it generates a message containing @@ -624,7 +592,7 @@ class MailThread(models.AbstractModel): tracking = dict() for record in self: try: - tracking[record.id] = record._message_track(tracked_fields, initial_values[record.id]) + tracking[record.id] = record._mail_track(tracked_fields, initial_values[record.id]) except MissingError: continue @@ -648,13 +616,6 @@ class MailThread(models.AbstractModel): return tracking - def _message_track(self, tracked_fields, initial): - """ For a given record, fields to check (tuple column name, column info) - and initial values, return a structure that is a tuple containing : - - a set of updated column names - - a list of ORM (0, 0, values) commands to create 'mail.tracking.value' """ - return self.static_message_track(self, tracked_fields, initial) - def _track_subtype(self, init_values): """ Give the subtypes triggered by the changes on the record according to values that have been updated. @@ -835,13 +796,10 @@ class MailThread(models.AbstractModel): obj = self.env[alias.alias_parent_model_id.model].browse(alias.alias_parent_thread_id) else: obj = self.env[model] - if hasattr(obj, '_alias_check_contact'): - error_code = obj._alias_check_contact(message, message_dict, alias) - else: - error_code = self.env['mail.alias.mixin']._alias_check_contact_on_record(obj, message, message_dict, alias) - if error_code is not True: + error_message = obj._alias_get_error_message(message, message_dict, alias) + if error_message: self._routing_warn( - _('alias %(name)s: %(error)s', name=alias.alias_name, error=error_code or _('unknown error')), + _('alias %(name)s: %(error)s', name=alias.alias_name, error=error_message or _('unknown error')), message_id, route, False @@ -1480,30 +1438,6 @@ class MailThread(models.AbstractModel): # RECIPIENTS MANAGEMENT TOOLS # ------------------------------------------------------ - @api.model - def _message_get_default_recipients_on_records(self, records): - """ Generic implementation for finding default recipient to mail on - a recordset. ``_message_get_default_recipients`` may be defined to - implement custom behavior. """ - if hasattr(records, '_message_get_default_recipients'): - return records._message_get_default_recipients() - - res = {} - for record in records: - recipient_ids, email_to, email_cc = [], False, False - if 'partner_id' in record and record.partner_id: - recipient_ids.append(record.partner_id.id) - elif 'email_normalized' in record and record.email_normalized: - email_to = record.email_normalized - elif 'email_from' in record and record.email_from: - email_to = record.email_from - elif 'partner_email' in record and record.partner_email: - email_to = record.partner_email - elif 'email' in record and record.email: - email_to = record.email - res[record.id] = {'partner_ids': recipient_ids, 'email_to': email_to, 'email_cc': email_cc} - return res - def _message_add_suggested_recipient(self, result, partner=None, email=None, reason=''): """ Called by _message_get_suggested_recipients, to add a suggested recipient in the result dictionary. The form is : @@ -2637,90 +2571,6 @@ class MailThread(models.AbstractModel): return result - def _notify_get_reply_to(self, default=None, records=None, company=None, doc_names=None): - """ Returns the preferred reply-to email address when replying to a thread - on documents. Documents are either given by self it this method is called - as a true multi method on a record set or can be given using records to - have a generic implementation available for all models. - Reply-to is formatted like "MyCompany MyDocument ". - Heuristic it the following: - * search for specific aliases as they always have priority; it is limited - to aliases linked to documents (like project alias for task for example); - * use catchall address; - * use default; - This method works on documents - * as a true multi method for models inheriting from mail.thread; call - ``records._notify_get_reply_to(...)``; - * as a generic implementation if records are given; call ``MailThread. - _notify_get_reply_to(records=records, ...)``; - * as a generic implementation is self is a void mail.thread record set - meaning catchall is computed; call ``MailThread._notify_get_reply_to - (records=None)``; - Tweak this method on a specific model by overriding if it inherits from - mail.thread. An example would be tasks taking their reply-to alias from - their project. - :param default: default email if no alias or catchall is found; - :param records: record_set if self if a generic mail.thread record allowing - generic implementation of finding reply-to; - :param company: used to compute company name part of the from name; provide - it if already known, otherwise fall back on user company; - :param doc_names: dict(res_id, doc_name) used to compute doc name part of - the from name; provide it if already known to avoid queries, otherwise - name_get on document will be performed; - :return result: dictionary. Keys are record IDs and value is formatted - like an email "Company_name Document_name "/ - """ - _records = self if self and self._name != 'mail.thread' else records - model = _records._name if _records and _records._name != 'mail.thread' else False - res_ids = _records.ids if _records and model else [] - _res_ids = res_ids or [False] # always have a default value located in False - - alias_domain = self.env['ir.config_parameter'].sudo().get_param("mail.catchall.domain") - result = dict.fromkeys(_res_ids, False) - result_email = dict() - doc_names = doc_names if doc_names else dict() - - if alias_domain: - if model and res_ids: - if not doc_names: - doc_names = dict((rec.id, rec.display_name) for rec in _records) - - mail_aliases = self.env['mail.alias'].sudo().search([ - ('alias_parent_model_id.model', '=', model), - ('alias_parent_thread_id', 'in', res_ids), - ('alias_name', '!=', False)]) - # take only first found alias for each thread_id, to match order (1 found -> limit=1 for each res_id) - for alias in mail_aliases: - result_email.setdefault(alias.alias_parent_thread_id, '%s@%s' % (alias.alias_name, alias_domain)) - - # left ids: use catchall - left_ids = set(_res_ids) - set(result_email) - if left_ids: - catchall = self.env['ir.config_parameter'].sudo().get_param("mail.catchall.alias") - if catchall: - result_email.update(dict((rid, '%s@%s' % (catchall, alias_domain)) for rid in left_ids)) - - # compute name of reply-to - TDE tocheck: quotes and stuff like that - company_name = company.name if company else self.env.company.name - for res_id in result_email.keys(): - name = '%s%s%s' % (company_name, ' ' if doc_names.get(res_id) else '', doc_names.get(res_id, '')) - result[res_id] = tools.formataddr((name, result_email[res_id])) - - left_ids = set(_res_ids) - set(result_email) - if left_ids: - result.update(dict((res_id, default) for res_id in left_ids)) - - return result - - @api.model - def _notify_get_reply_to_on_records(self, default=None, records=None, company=None, doc_names=None): - """ Generic wrapper on ``_notify_get_reply_to`` checking mail.thread inheritance - and allowing to call model-specific implementation in a one liner. This - method should not be overridden. """ - if records and hasattr(records, '_notify_get_reply_to'): - return records._notify_get_reply_to(default=default, company=company, doc_names=doc_names) - return self._notify_get_reply_to(default=default, records=records, company=company, doc_names=doc_names) - def _notify_email_recipient_values(self, recipient_ids): """ Format email notification recipient values to store on the notification mail.mail. Basic method just set the recipient partners as mail_mail diff --git a/addons/mail/models/models.py b/addons/mail/models/models.py index 1159a7ff6fd..f68cf42ed5f 100644 --- a/addons/mail/models/models.py +++ b/addons/mail/models/models.py @@ -3,15 +3,176 @@ from lxml.builder import E -from odoo import models, api +from odoo import api, models, tools, _ class BaseModel(models.AbstractModel): _inherit = 'base' + # ------------------------------------------------------------ + # GENERIC MAIL FEATURES + # ------------------------------------------------------------ + + def _mail_track(self, tracked_fields, initial): + """ For a given record, fields to check (tuple column name, column info) + and initial values, return a valid command to create tracking values. + + :param tracked_fields: fields_get of updated fields on which tracking + is checked and performed; + :param initial: dict of initial values for each updated fields; + + :return: a tuple (changes, tracking_value_ids) where + changes: set of updated column names; + tracking_value_ids: a list of ORM (0, 0, values) commands to create + ``mail.tracking.value`` records; + + Override this method on a specific model to implement model-specific + behavior. Also consider inheriting from ``mail.thread``. """ + self.ensure_one() + changes = set() # contains onchange tracked fields that changed + tracking_value_ids = [] + + # generate tracked_values data structure: {'col_name': {col_info, new_value, old_value}} + for col_name, col_info in tracked_fields.items(): + if col_name not in initial: + continue + initial_value = initial[col_name] + new_value = self[col_name] + + if new_value != initial_value and (new_value or initial_value): # because browse null != False + tracking_sequence = getattr(self._fields[col_name], 'tracking', + getattr(self._fields[col_name], 'track_sequence', 100)) # backward compatibility with old parameter name + if tracking_sequence is True: + tracking_sequence = 100 + tracking = self.env['mail.tracking.value'].create_tracking_values(initial_value, new_value, col_name, col_info, tracking_sequence, self._name) + if tracking: + tracking_value_ids.append([0, 0, tracking]) + changes.add(col_name) + + return changes, tracking_value_ids + + def _message_get_default_recipients(self): + """ Generic implementation for finding default recipient to mail on + a recordset. This method is a generic implementation available for + all models as we could send an email through mail templates on models + not inheriting from mail.thread. + + Override this method on a specific model to implement model-specific + behavior. Also consider inheriting from ``mail.thread``. """ + res = {} + for record in self: + recipient_ids, email_to, email_cc = [], False, False + if 'partner_id' in record and record.partner_id: + recipient_ids.append(record.partner_id.id) + elif 'email_normalized' in record and record.email_normalized: + email_to = record.email_normalized + elif 'email_from' in record and record.email_from: + email_to = record.email_from + elif 'partner_email' in record and record.partner_email: + email_to = record.partner_email + elif 'email' in record and record.email: + email_to = record.email + res[record.id] = {'partner_ids': recipient_ids, 'email_to': email_to, 'email_cc': email_cc} + return res + + def _notify_get_reply_to(self, default=None, records=None, company=None, doc_names=None): + """ Returns the preferred reply-to email address when replying to a thread + on documents. This method is a generic implementation available for + all models as we could send an email through mail templates on models + not inheriting from mail.thread. + + Reply-to is formatted like "MyCompany MyDocument ". + Heuristic it the following: + * search for specific aliases as they always have priority; it is limited + to aliases linked to documents (like project alias for task for example); + * use catchall address; + * use default; + + This method can be used as a generic tools if self is a void recordset. + + Override this method on a specific model to implement model-specific + behavior. Also consider inheriting from ``mail.thread``. + An example would be tasks taking their reply-to alias from their project. + + :param default: default email if no alias or catchall is found; + :param records: DEPRECATED, self should be a valid record set or an + empty recordset if a generic reply-to is required; + :param company: used to compute company name part of the from name; provide + it if already known, otherwise fall back on user company; + :param doc_names: dict(res_id, doc_name) used to compute doc name part of + the from name; provide it if already known to avoid queries, otherwise + name_get on document will be performed; + :return result: dictionary. Keys are record IDs and value is formatted + like an email "Company_name Document_name "/ + """ + if records: + raise ValueError('Use of records is deprecated as this method is available on BaseModel.') + + _records = self + model = _records._name if _records and _records._name != 'mail.thread' else False + res_ids = _records.ids if _records and model else [] + _res_ids = res_ids or [False] # always have a default value located in False + + alias_domain = self.env['ir.config_parameter'].sudo().get_param("mail.catchall.domain") + result = dict.fromkeys(_res_ids, False) + result_email = dict() + doc_names = doc_names if doc_names else dict() + + if alias_domain: + if model and res_ids: + if not doc_names: + doc_names = dict((rec.id, rec.display_name) for rec in _records) + + mail_aliases = self.env['mail.alias'].sudo().search([ + ('alias_parent_model_id.model', '=', model), + ('alias_parent_thread_id', 'in', res_ids), + ('alias_name', '!=', False)]) + # take only first found alias for each thread_id, to match order (1 found -> limit=1 for each res_id) + for alias in mail_aliases: + result_email.setdefault(alias.alias_parent_thread_id, '%s@%s' % (alias.alias_name, alias_domain)) + + # left ids: use catchall + left_ids = set(_res_ids) - set(result_email) + if left_ids: + catchall = self.env['ir.config_parameter'].sudo().get_param("mail.catchall.alias") + if catchall: + result_email.update(dict((rid, '%s@%s' % (catchall, alias_domain)) for rid in left_ids)) + + # compute name of reply-to - TDE tocheck: quotes and stuff like that + company_name = company.name if company else self.env.company.name + for res_id in result_email: + name = '%s%s%s' % (company_name, ' ' if doc_names.get(res_id) else '', doc_names.get(res_id, '')) + result[res_id] = tools.formataddr((name, result_email[res_id])) + + left_ids = set(_res_ids) - set(result_email) + if left_ids: + result.update(dict((res_id, default) for res_id in left_ids)) + + return result + + # ------------------------------------------------------------ + # ALIAS MANAGEMENT + # ------------------------------------------------------------ + + def _alias_get_error_message(self, message, message_dict, alias): + """ Generic method that takes a record not necessarily inheriting from + mail.alias.mixin. """ + author = self.env['res.partner'].browse(message_dict.get('author_id', False)) + if alias.alias_contact == 'followers': + if not self.ids: + return _('incorrectly configured alias (unknown reference record)') + if not hasattr(self, "message_partner_ids") or not hasattr(self, "message_channel_ids"): + return _('incorrectly configured alias') + accepted_partner_ids = self.message_partner_ids | self.message_channel_ids.mapped('channel_partner_ids') + if not author or author not in accepted_partner_ids: + return _('restricted to followers') + elif alias.alias_contact == 'partners' and not author: + return _('restricted to known authors') + return False + # ------------------------------------------------------------ # ACTIVITY - # ----------------------------------------------------------- + # ------------------------------------------------------------ @api.model def _get_default_activity_view(self): diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index f66bdba6557..e9df4a0cb80 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -278,7 +278,7 @@ class MailComposer(models.TransientModel): reply_to_value = dict.fromkeys(res_ids, None) if mass_mail_mode and not self.no_auto_thread: records = self.env[self.model].browse(res_ids) - reply_to_value = self.env['mail.thread']._notify_get_reply_to_on_records(default=self.email_from, records=records) + reply_to_value = records._notify_get_reply_to(default=self.email_from) blacklisted_rec_ids = [] if mass_mail_mode and issubclass(type(self.env[self.model]), self.pool['mail.thread.blacklist']): @@ -462,7 +462,7 @@ class MailComposer(models.TransientModel): default_recipients = {} if not self.partner_ids: records = self.env[self.model].browse(res_ids).sudo() - default_recipients = self.env['mail.thread']._message_get_default_recipients_on_records(records) + default_recipients = records._message_get_default_recipients() results = dict.fromkeys(res_ids, False) for res_id in res_ids: