summaryrefslogtreecommitdiff
path: root/addons/mail
diff options
context:
space:
mode:
authorThibault Delavallée <tde@odoo.com>2020-08-27 12:23:52 +0000
committerThibault Delavallée <tde@odoo.com>2020-08-27 14:29:48 +0000
commit4a5fc70cfc3c86da33e2480fbd59c8f6d575c6e9 (patch)
tree46055e053a8a66dfb47424970fba33d9fda7c589 /addons/mail
parente4e4ee3d03c887900983b815abc8e349359c265c (diff)
[REF] mail: reorganize a bit 13+ thread code and set some methods private
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 Clearly separate CRUD / CRUD HELPERS / TRACKING / ... . Notably tracking code was split accross two code sections. Make some internal tools methods private. They do not require to be public * with_lang: does not makes sense to be available outside of odoo. It is renamed to _fallback_lang as it does not allow to set a specific lang in the environment like with_user. It is used to fallback on user's lang in context; * get_mail_message_access: purely internal method used for access rights; LINKS Task ID-2327096 (code cleaning) PR #56631
Diffstat (limited to 'addons/mail')
-rw-r--r--addons/mail/models/mail_message.py6
-rw-r--r--addons/mail/models/mail_thread.py234
-rw-r--r--addons/mail/models/models.py13
3 files changed, 139 insertions, 114 deletions
diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py
index ba57397a7c7..f74596c1226 100644
--- a/addons/mail/models/mail_message.py
+++ b/addons/mail/models/mail_message.py
@@ -514,10 +514,10 @@ class Message(models.Model):
model_record_ids = _generate_model_record_ids(message_values, document_related_candidate_ids)
for model, doc_ids in model_record_ids.items():
DocumentModel = self.env[model]
- if hasattr(DocumentModel, 'get_mail_message_access'):
- check_operation = DocumentModel.get_mail_message_access(doc_ids, operation) ## why not giving model here?
+ if hasattr(DocumentModel, '_get_mail_message_access'):
+ check_operation = DocumentModel._get_mail_message_access(doc_ids, operation) ## why not giving model here?
else:
- check_operation = self.env['mail.thread'].get_mail_message_access(doc_ids, operation, model_name=model)
+ check_operation = self.env['mail.thread']._get_mail_message_access(doc_ids, operation, model_name=model)
records = DocumentModel.browse(doc_ids)
records.check_access_rights(check_operation)
mids = records.browse(doc_ids)._filter_access_rules(check_operation)
diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index 6350732dc28..22497352bec 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -112,10 +112,6 @@ class MailThread(models.AbstractModel):
message_attachment_count = fields.Integer('Attachment Count', compute='_compute_message_attachment_count', groups="base.group_user")
message_main_attachment_id = fields.Many2one(string="Main Attachment", comodel_name='ir.attachment', index=True, copy=False)
- def _valid_field_parameter(self, field, name):
- # allow tracking on models inheriting from 'mail.thread'
- return name == 'tracking' or super()._valid_field_parameter(field, name)
-
@api.depends('message_follower_ids')
def _get_followers(self):
for thread in self:
@@ -247,9 +243,9 @@ class MailThread(models.AbstractModel):
for record in self:
record.message_attachment_count = attachment_count_dict.get(record.id, 0)
- # ------------------------------------------------------
- # CRUD overrides for automatic subscription and logging
- # ------------------------------------------------------
+ # ------------------------------------------------------------
+ # CRUD
+ # ------------------------------------------------------------
@api.model_create_multi
def create(self, vals_list):
@@ -326,12 +322,6 @@ class MailThread(models.AbstractModel):
return result
- def _compute_field_value(self, field):
- if not self._context.get('tracking_disable') and not self._context.get('mail_notrack'):
- self._prepare_tracking(f.name for f in self.pool.field_computed[field] if f.store)
-
- return super()._compute_field_value(field)
-
def unlink(self):
""" Override unlink to delete messages and followers. This cannot be
cascaded, because link is done through (res_model, res_id). """
@@ -408,62 +398,37 @@ class MailThread(models.AbstractModel):
return help
# ------------------------------------------------------
- # Technical methods / wrappers / tools
+ # MODELS / CRUD HELPERS
# ------------------------------------------------------
- def _prepare_tracking(self, fields):
- """ Prepare the tracking of ``fields`` for ``self``.
+ def _compute_field_value(self, field):
+ if not self._context.get('tracking_disable') and not self._context.get('mail_notrack'):
+ self._prepare_tracking(f.name for f in self.pool.field_computed[field] if f.store)
- :param fields: iterable of fields names to potentially track
- """
- fnames = self._get_tracked_fields().intersection(fields)
- if not fnames:
- return
- func = self.browse()._finalize_tracking
- [initial_values] = self.env.cr.precommit.add(func, dict)
- for record in self:
- if not record.id:
- continue
- values = initial_values.setdefault(record.id, {})
- if values is not None:
- for fname in fnames:
- values.setdefault(fname, record[fname])
+ return super()._compute_field_value(field)
- def _discard_tracking(self):
- """ Prevent any tracking of fields on ``self``. """
- if not self._get_tracked_fields():
- return
- func = self.browse()._finalize_tracking
- [initial_values] = self.env.cr.precommit.add(func, dict)
- # disable tracking by setting initial values to None
- for id_ in self.ids:
- initial_values[id_] = None
+ def _creation_subtype(self):
+ """ Give the subtypes triggered by the creation of a record
- def _finalize_tracking(self, initial_values):
- """ Generate the tracking messages for the records that have been
- prepared with ``_prepare_tracking``.
+ :returns: a subtype browse record (empty if no subtype is triggered)
"""
- ids = [id_ for id_, vals in initial_values.items() if vals]
- if not ids:
- return
- records = self.browse(ids).sudo()
- fnames = self._get_tracked_fields()
- context = clean_context(self._context)
- tracking = records.with_context(context).message_track(fnames, initial_values)
- for record in records:
- changes, tracking_value_ids = tracking.get(record.id, (None, None))
- record._message_track_post_template(changes)
- # this method is called after the main flush() and just before commit();
- # we have to flush() again in case we triggered some recomputations
- self.flush()
+ return self.env['mail.message.subtype']
- def with_lang(self):
- if not self._context.get("lang"):
- return self.with_context(lang=self.env.user.lang)
- return self
+ def _get_creation_message(self):
+ """ Get the creation message to log into the chatter at the record's creation.
+ :returns: The message's body to log.
+ """
+ self.ensure_one()
+ doc_name = self.env['ir.model']._get(self._name).name
+ return _('%s created', doc_name)
@api.model
def get_mail_message_access(self, res_ids, operation, model_name=None):
+ """ Deprecated, remove with v14+ """
+ return self._get_mail_message_access(res_ids, operation, model_name=model_name)
+
+ @api.model
+ def _get_mail_message_access(self, res_ids, operation, model_name=None):
""" mail.message check permission rules for related document. This method is
meant to be inherited in order to implement addons-specific behavior.
A common behavior would be to allow creating messages when having read
@@ -482,6 +447,23 @@ class MailThread(models.AbstractModel):
check_operation = operation
return check_operation
+ def _valid_field_parameter(self, field, name):
+ # allow tracking on models inheriting from 'mail.thread'
+ return name == 'tracking' or super()._valid_field_parameter(field, name)
+
+ def with_lang(self):
+ """ Deprecated, remove in 14+ """
+ return self._fallback_lang()
+
+ def _fallback_lang(self):
+ if not self._context.get("lang"):
+ return self.with_context(lang=self.env.user.lang)
+ return self
+
+ # ------------------------------------------------------
+ # WRAPPERS AND TOOLS
+ # ------------------------------------------------------
+
def message_change_thread(self, new_thread):
"""
Transfer the list of the mail thread messages from an model to another
@@ -517,9 +499,55 @@ class MailThread(models.AbstractModel):
return True
# ------------------------------------------------------
- # Automatic log / Tracking
+ # TRACKING / LOG
# ------------------------------------------------------
+ def _prepare_tracking(self, fields):
+ """ Prepare the tracking of ``fields`` for ``self``.
+
+ :param fields: iterable of fields names to potentially track
+ """
+ fnames = self._get_tracked_fields().intersection(fields)
+ if not fnames:
+ return
+ func = self.browse()._finalize_tracking
+ [initial_values] = self.env.cr.precommit.add(func, dict)
+ for record in self:
+ if not record.id:
+ continue
+ values = initial_values.setdefault(record.id, {})
+ if values is not None:
+ for fname in fnames:
+ values.setdefault(fname, record[fname])
+
+ def _discard_tracking(self):
+ """ Prevent any tracking of fields on ``self``. """
+ if not self._get_tracked_fields():
+ return
+ func = self.browse()._finalize_tracking
+ [initial_values] = self.env.cr.precommit.add(func, dict)
+ # disable tracking by setting initial values to None
+ for id_ in self.ids:
+ initial_values[id_] = None
+
+ def _finalize_tracking(self, initial_values):
+ """ Generate the tracking messages for the records that have been
+ prepared with ``_prepare_tracking``.
+ """
+ ids = [id_ for id_, vals in initial_values.items() if vals]
+ if not ids:
+ return
+ records = self.browse(ids).sudo()
+ fnames = self._get_tracked_fields()
+ context = clean_context(self._context)
+ tracking = records.with_context(context).message_track(fnames, initial_values)
+ for record in records:
+ changes, tracking_value_ids = tracking.get(record.id, (None, None))
+ record._message_track_post_template(changes)
+ # this method is called after the main flush() and just before commit();
+ # we have to flush() again in case we triggered some recomputations
+ self.flush()
+
@tools.ormcache('self.env.uid', 'self.env.su')
def _get_tracked_fields(self):
""" Return the set of tracked fields names for the current model. """
@@ -531,35 +559,6 @@ class MailThread(models.AbstractModel):
return fields and set(self.fields_get(fields))
- def _creation_subtype(self):
- """ Give the subtypes triggered by the creation of a record
-
- :returns: a subtype browse record (empty if no subtype is triggered)
- """
- return self.env['mail.message.subtype']
-
- def _get_creation_message(self):
- """ Get the creation message to log into the chatter at the record's creation.
- :returns: The message's body to log.
- """
- self.ensure_one()
- doc_name = self.env['ir.model']._get(self._name).name
- return _('%s created', doc_name)
-
- def _track_subtype(self, init_values):
- """ Give the subtypes triggered by the changes on the record according
- to values that have been updated.
-
- :param init_values: the original values of the record; only modified fields
- are present in the dict
- :type init_values: dict
- :returns: a subtype browse record or False if no subtype is trigerred
- """
- return False
-
- def _track_template(self, changes):
- return dict()
-
def _message_track_post_template(self, changes):
if not changes:
return True
@@ -576,19 +575,20 @@ class MailThread(models.AbstractModel):
if not template:
continue
if isinstance(template, str):
- self.with_lang().message_post_with_view(template, **post_kwargs)
+ self._fallback_lang().message_post_with_view(template, **post_kwargs)
else:
- self.with_lang().message_post_with_template(template.id, **post_kwargs)
+ self._fallback_lang().message_post_with_template(template.id, **post_kwargs)
return True
+ 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.
"""
@@ -615,14 +615,6 @@ class MailThread(models.AbstractModel):
return changes, tracking_value_ids
- 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 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
@@ -665,8 +657,26 @@ 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.
+
+ :param init_values: the original values of the record; only modified fields
+ are present in the dict
+ :type init_values: dict
+ :returns: a subtype browse record or False if no subtype is trigerred
+ """
+ return False
+
# ------------------------------------------------------
- # Mail gateway
+ # MAIL GATEWAY
# ------------------------------------------------------
def _routing_warn(self, error_message, message_id, route, raise_exception=True):
@@ -1476,7 +1486,7 @@ class MailThread(models.AbstractModel):
return msg_dict
# ------------------------------------------------------
- # Recipient management
+ # RECIPIENTS MANAGEMENT TOOLS
# ------------------------------------------------------
@api.model
@@ -1688,7 +1698,7 @@ class MailThread(models.AbstractModel):
return result
# ------------------------------------------------------
- # Post / Send message API
+ # MESSAGE POST API
# ------------------------------------------------------
def _message_post_process_attachments(self, attachments, attachment_ids, message_values):
@@ -1837,7 +1847,7 @@ class MailThread(models.AbstractModel):
if 'subtype' in kwargs:
raise ValueError("message_post doesn't support subtype parameter anymore. Please give a valid subtype_id or subtype_xmlid value instead.")
- self = self.with_lang() # add lang to context imediatly since it will be usefull in various flows latter.
+ self = self._fallback_lang() # add lang to context imediatly since it will be usefull in various flows latter.
# Explicit access rights check, because display_name is computed as sudo.
self.check_access_rights('read')
@@ -1928,6 +1938,10 @@ class MailThread(models.AbstractModel):
using already-computed values instead of having to rebrowse things. """
pass
+ # ------------------------------------------------------
+ # MESSAGE POST TOOLS
+ # ------------------------------------------------------
+
def message_post_with_view(self, views_or_xmlid, **kwargs):
""" Helper method to send a mail / post a message using a view_id to
render using the ir.qweb engine. This method is stand alone, because
@@ -2121,7 +2135,7 @@ class MailThread(models.AbstractModel):
return self.env['mail.message'].create(create_values_list)
# ------------------------------------------------------
- # Notification API
+ # NOTIFICATION API
# ------------------------------------------------------
def _notify_thread(self, message, msg_vals=False, notify_by_email=True, **kwargs):
@@ -2231,7 +2245,7 @@ class MailThread(models.AbstractModel):
return True
model = msg_vals.get('model') if msg_vals else message.model
- model_name = model_description or (self.with_lang().env['ir.model']._get(model).display_name if model else False) # one query for display name
+ model_name = model_description or (self._fallback_lang().env['ir.model']._get(model).display_name if model else False) # one query for display name
recipients_groups_data = self._notify_classify_recipients(partners_data, model_name)
if not recipients_groups_data:
@@ -2729,7 +2743,7 @@ class MailThread(models.AbstractModel):
}
# ------------------------------------------------------
- # Followers API
+ # FOLLOWERS API
# ------------------------------------------------------
def message_subscribe(self, partner_ids=None, channel_ids=None, subtype_ids=None):
@@ -2943,7 +2957,7 @@ class MailThread(models.AbstractModel):
return True
# ------------------------------------------------------
- # Controllers
+ # CONTROLLERS
# ------------------------------------------------------
def _get_mail_redirect_suggested_company(self):
@@ -2951,7 +2965,7 @@ class MailThread(models.AbstractModel):
in case of a mail redirection to the record. To avoid multi
company issues when clicking on a link sent by email, this
could be called to try setting the most suited company on
- the allowed_company_ids in the context. This method can be
+ the allowed_company_ids in the context. This method can be
overridden, for example on the hr.leave model, where the
most suited company is the company of the leave type, as
specified by the ir.rule.
diff --git a/addons/mail/models/models.py b/addons/mail/models/models.py
index 8c407ea4535..1159a7ff6fd 100644
--- a/addons/mail/models/models.py
+++ b/addons/mail/models/models.py
@@ -1,11 +1,18 @@
# -*- coding: utf-8 -*-
-from odoo import models, api
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
from lxml.builder import E
+from odoo import models, api
+
class BaseModel(models.AbstractModel):
_inherit = 'base'
+ # ------------------------------------------------------------
+ # ACTIVITY
+ # -----------------------------------------------------------
+
@api.model
def _get_default_activity_view(self):
""" Generates an empty activity view.
@@ -18,6 +25,10 @@ class BaseModel(models.AbstractModel):
templates = E.templates(activity_box)
return E.activity(templates, string=self._description)
+ # ------------------------------------------------------------
+ # GATEWAY: NOTIFICATION
+ # ------------------------------------------------------------
+
def _notify_email_headers(self):
"""
Generate the email headers based on record