[MOV] mail: reorder a bit mail thread methods by category
As this class has grown a lot some methods have been put a bit anywhere in the file. To prepare future improvements it is easier to regroup them a bit. remove_accent from mail_alias is also moved to tools in order to separate tools from business or class specific code.
This commit is contained in:
@@ -3,26 +3,15 @@
|
||||
|
||||
import logging
|
||||
import re
|
||||
import unicodedata
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools import ustr
|
||||
from odoo.tools import remove_accents
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Inspired by http://stackoverflow.com/questions/517923
|
||||
def remove_accents(input_str):
|
||||
"""Suboptimal-but-better-than-nothing way to replace accented
|
||||
latin letters by an ASCII equivalent. Will obviously change the
|
||||
meaning of input_str and work only for some cases"""
|
||||
input_str = ustr(input_str)
|
||||
nkfd_form = unicodedata.normalize('NFKD', input_str)
|
||||
return u''.join([c for c in nkfd_form if not unicodedata.combining(c)])
|
||||
|
||||
|
||||
class Alias(models.Model):
|
||||
"""A Mail Alias is a mapping of an email address with a given Odoo Document
|
||||
model. It is used by Odoo's mail gateway when processing incoming emails
|
||||
|
||||
+115
-119
@@ -298,10 +298,6 @@ class MailThread(models.AbstractModel):
|
||||
# avoid tracking multiple temporary changes during copy
|
||||
return super(MailThread, self.with_context(mail_notrack=True)).copy_data(default=default)
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Technical methods (to clean / move to controllers ?)
|
||||
# ------------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def get_empty_list_help(self, help):
|
||||
""" Override of BaseModel.get_empty_list_help() to generate an help message
|
||||
@@ -372,6 +368,89 @@ class MailThread(models.AbstractModel):
|
||||
res['arch'] = etree.tostring(doc, encoding='unicode')
|
||||
return res
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Technical methods / wrappers / tools
|
||||
# ------------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def _garbage_collect_attachments(self):
|
||||
""" Garbage collect lost mail attachments. Those are attachments
|
||||
- linked to res_model 'mail.compose.message', the composer wizard
|
||||
- with res_id 0, because they were created outside of an existing
|
||||
wizard (typically user input through Chatter or reports
|
||||
created on-the-fly by the templates)
|
||||
- unused since at least one day (create_date and write_date)
|
||||
"""
|
||||
limit_date = datetime.datetime.utcnow() - datetime.timedelta(days=1)
|
||||
limit_date_str = datetime.datetime.strftime(limit_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.env['ir.attachment'].search([
|
||||
('res_model', '=', 'mail.compose.message'),
|
||||
('res_id', '=', 0),
|
||||
('create_date', '<', limit_date_str),
|
||||
('write_date', '<', limit_date_str)]
|
||||
).unlink()
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def check_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
|
||||
access rule on the document, for portal document such as issues. """
|
||||
if model_name:
|
||||
DocModel = self.env[model_name]
|
||||
else:
|
||||
DocModel = self
|
||||
if hasattr(DocModel, '_mail_post_access'):
|
||||
create_allow = DocModel._mail_post_access
|
||||
else:
|
||||
create_allow = 'write'
|
||||
|
||||
if operation in ['write', 'unlink']:
|
||||
check_operation = 'write'
|
||||
elif operation == 'create' and create_allow in ['create', 'read', 'write', 'unlink']:
|
||||
check_operation = create_allow
|
||||
elif operation == 'create':
|
||||
check_operation = 'write'
|
||||
else:
|
||||
check_operation = operation
|
||||
|
||||
DocModel.check_access_rights(check_operation)
|
||||
DocModel.browse(res_ids).check_access_rule(check_operation)
|
||||
|
||||
@api.multi
|
||||
def message_change_thread(self, new_thread):
|
||||
"""
|
||||
Transfer the list of the mail thread messages from an model to another
|
||||
|
||||
:param id : the old res_id of the mail.message
|
||||
:param new_res_id : the new res_id of the mail.message
|
||||
:param new_model : the name of the new model of the mail.message
|
||||
|
||||
Example : my_lead.message_change_thread(my_project_task)
|
||||
will transfer the context of the thread of my_lead to my_project_task
|
||||
"""
|
||||
self.ensure_one()
|
||||
# get the subtype of the comment Message
|
||||
subtype_comment = self.env.ref('mail.mt_comment')
|
||||
|
||||
# get the ids of the comment and not-comment of the thread
|
||||
# TDE check: sudo on mail.message, to be sure all messages are moved ?
|
||||
MailMessage = self.env['mail.message']
|
||||
msg_comment = MailMessage.search([
|
||||
('model', '=', self._name),
|
||||
('res_id', '=', self.id),
|
||||
('subtype_id', '=', subtype_comment.id)])
|
||||
msg_not_comment = MailMessage.search([
|
||||
('model', '=', self._name),
|
||||
('res_id', '=', self.id),
|
||||
('subtype_id', '!=', subtype_comment.id)])
|
||||
|
||||
# update the messages
|
||||
msg_comment.write({"res_id": new_thread.id, "model": new_thread._name})
|
||||
msg_not_comment.write({"res_id": new_thread.id, "model": new_thread._name, "subtype_id": None})
|
||||
return True
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Automatic log / Tracking
|
||||
# ------------------------------------------------------
|
||||
@@ -504,55 +583,9 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
return True
|
||||
|
||||
#------------------------------------------------------
|
||||
# mail.message wrappers and tools
|
||||
#------------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def _garbage_collect_attachments(self):
|
||||
""" Garbage collect lost mail attachments. Those are attachments
|
||||
- linked to res_model 'mail.compose.message', the composer wizard
|
||||
- with res_id 0, because they were created outside of an existing
|
||||
wizard (typically user input through Chatter or reports
|
||||
created on-the-fly by the templates)
|
||||
- unused since at least one day (create_date and write_date)
|
||||
"""
|
||||
limit_date = datetime.datetime.utcnow() - datetime.timedelta(days=1)
|
||||
limit_date_str = datetime.datetime.strftime(limit_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.env['ir.attachment'].search([
|
||||
('res_model', '=', 'mail.compose.message'),
|
||||
('res_id', '=', 0),
|
||||
('create_date', '<', limit_date_str),
|
||||
('write_date', '<', limit_date_str)]
|
||||
).unlink()
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def check_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
|
||||
access rule on the document, for portal document such as issues. """
|
||||
if model_name:
|
||||
DocModel = self.env[model_name]
|
||||
else:
|
||||
DocModel = self
|
||||
if hasattr(DocModel, '_mail_post_access'):
|
||||
create_allow = DocModel._mail_post_access
|
||||
else:
|
||||
create_allow = 'write'
|
||||
|
||||
if operation in ['write', 'unlink']:
|
||||
check_operation = 'write'
|
||||
elif operation == 'create' and create_allow in ['create', 'read', 'write', 'unlink']:
|
||||
check_operation = create_allow
|
||||
elif operation == 'create':
|
||||
check_operation = 'write'
|
||||
else:
|
||||
check_operation = operation
|
||||
|
||||
DocModel.check_access_rights(check_operation)
|
||||
DocModel.browse(res_ids).check_access_rule(check_operation)
|
||||
# ------------------------------------------------------
|
||||
# Email Notification
|
||||
# ------------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def _generate_notification_token(self, base_link, params):
|
||||
@@ -706,32 +739,6 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
return result
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Email specific
|
||||
# ------------------------------------------------------
|
||||
|
||||
@api.multi
|
||||
def message_get_default_recipients(self, res_model=None, res_ids=None):
|
||||
if res_model and res_ids:
|
||||
if hasattr(self.env[res_model], 'message_get_default_recipients'):
|
||||
return self.env[res_model].browse(res_ids).message_get_default_recipients()
|
||||
records = self.env[res_model].sudo().browse(res_ids)
|
||||
else:
|
||||
records = self.sudo()
|
||||
res = {}
|
||||
for record in records:
|
||||
recipient_ids, email_to, email_cc = set(), False, False
|
||||
if 'partner_id' in self._fields and record.partner_id:
|
||||
recipient_ids.add(record.partner_id.id)
|
||||
elif 'email_from' in self._fields and record.email_from:
|
||||
email_to = record.email_from
|
||||
elif 'partner_email' in self._fields and record.partner_email:
|
||||
email_to = record.partner_email
|
||||
elif 'email' in self._fields:
|
||||
email_to = record.email
|
||||
res[record.id] = {'partner_ids': list(recipient_ids), 'email_to': email_to, 'email_cc': email_cc}
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def message_get_reply_to(self, res_ids, default=None):
|
||||
""" Returns the preferred reply-to email address that is basically the
|
||||
@@ -1550,9 +1557,31 @@ class MailThread(models.AbstractModel):
|
||||
msg_dict['body'], msg_dict['attachments'] = self._message_extract_payload(message, save_original=save_original)
|
||||
return msg_dict
|
||||
|
||||
#------------------------------------------------------
|
||||
# Note specific
|
||||
#------------------------------------------------------
|
||||
# ------------------------------------------------------
|
||||
# Recipient management
|
||||
# ------------------------------------------------------
|
||||
|
||||
@api.multi
|
||||
def message_get_default_recipients(self, res_model=None, res_ids=None):
|
||||
if res_model and res_ids:
|
||||
if hasattr(self.env[res_model], 'message_get_default_recipients'):
|
||||
return self.env[res_model].browse(res_ids).message_get_default_recipients()
|
||||
records = self.env[res_model].sudo().browse(res_ids)
|
||||
else:
|
||||
records = self.sudo()
|
||||
res = {}
|
||||
for record in records:
|
||||
recipient_ids, email_to, email_cc = set(), False, False
|
||||
if 'partner_id' in self._fields and record.partner_id:
|
||||
recipient_ids.add(record.partner_id.id)
|
||||
elif 'email_from' in self._fields and record.email_from:
|
||||
email_to = record.email_from
|
||||
elif 'partner_email' in self._fields and record.partner_email:
|
||||
email_to = record.partner_email
|
||||
elif 'email' in self._fields:
|
||||
email_to = record.email
|
||||
res[record.id] = {'partner_ids': list(recipient_ids), 'email_to': email_to, 'email_cc': email_cc}
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def _message_add_suggested_recipient(self, result, partner=None, email=None, reason=''):
|
||||
@@ -1689,6 +1718,10 @@ class MailThread(models.AbstractModel):
|
||||
]).write({'author_id': partner_info['partner_id']})
|
||||
return result
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Post / Send message API
|
||||
# ------------------------------------------------------
|
||||
|
||||
def _message_post_process_attachments(self, attachments, attachment_ids, message_data):
|
||||
""" Preprocess attachments for mail_thread.message_post() or mail_mail.create().
|
||||
|
||||
@@ -2148,40 +2181,3 @@ class MailThread(models.AbstractModel):
|
||||
self._message_auto_subscribe_notify(user_pids)
|
||||
|
||||
return True
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Thread management
|
||||
# ------------------------------------------------------
|
||||
|
||||
@api.multi
|
||||
def message_change_thread(self, new_thread):
|
||||
"""
|
||||
Transfer the list of the mail thread messages from an model to another
|
||||
|
||||
:param id : the old res_id of the mail.message
|
||||
:param new_res_id : the new res_id of the mail.message
|
||||
:param new_model : the name of the new model of the mail.message
|
||||
|
||||
Example : my_lead.message_change_thread(my_project_task)
|
||||
will transfer the context of the thread of my_lead to my_project_task
|
||||
"""
|
||||
self.ensure_one()
|
||||
# get the subtype of the comment Message
|
||||
subtype_comment = self.env.ref('mail.mt_comment')
|
||||
|
||||
# get the ids of the comment and not-comment of the thread
|
||||
# TDE check: sudo on mail.message, to be sure all messages are moved ?
|
||||
MailMessage = self.env['mail.message']
|
||||
msg_comment = MailMessage.search([
|
||||
('model', '=', self._name),
|
||||
('res_id', '=', self.id),
|
||||
('subtype_id', '=', subtype_comment.id)])
|
||||
msg_not_comment = MailMessage.search([
|
||||
('model', '=', self._name),
|
||||
('res_id', '=', self.id),
|
||||
('subtype_id', '!=', subtype_comment.id)])
|
||||
|
||||
# update the messages
|
||||
msg_comment.write({"res_id": new_thread.id, "model": new_thread._name})
|
||||
msg_not_comment.write({"res_id": new_thread.id, "model": new_thread._name, "subtype_id": None})
|
||||
return True
|
||||
|
||||
@@ -22,8 +22,10 @@ import sys
|
||||
import threading
|
||||
import time
|
||||
import types
|
||||
import unicodedata
|
||||
import werkzeug.utils
|
||||
import zipfile
|
||||
|
||||
from collections import defaultdict, Iterable, Mapping, MutableSet, OrderedDict
|
||||
from itertools import islice, groupby, repeat
|
||||
from lxml import etree
|
||||
@@ -732,6 +734,19 @@ def attrgetter(*items):
|
||||
return tuple(resolve_attr(obj, attr) for attr in items)
|
||||
return g
|
||||
|
||||
# ---------------------------------------------
|
||||
# String management
|
||||
# ---------------------------------------------
|
||||
|
||||
# Inspired by http://stackoverflow.com/questions/517923
|
||||
def remove_accents(input_str):
|
||||
"""Suboptimal-but-better-than-nothing way to replace accented
|
||||
latin letters by an ASCII equivalent. Will obviously change the
|
||||
meaning of input_str and work only for some cases"""
|
||||
input_str = ustr(input_str)
|
||||
nkfd_form = unicodedata.normalize('NFKD', input_str)
|
||||
return u''.join([c for c in nkfd_form if not unicodedata.combining(c)])
|
||||
|
||||
class unquote(str):
|
||||
"""A subclass of str that implements repr() without enclosing quotation marks
|
||||
or escaping, keeping the original string untouched. The name come from Lisp's unquote.
|
||||
|
||||
Reference in New Issue
Block a user