[REF] mail, various: clean thread _notify API
PURPOSE Global purpose is to rename some methods and add some docstrings to clean API of notification methods used in mail thread. Notably use _notify_thread prefix for main methods, and _notify_by_'mean' for tool sub-methods. SPECIFICATIONS Remove unused arguments coming from old implementation and usage. They were introduced notably for performance reason when cache was more often invalidated which is not the case anymore. Anyway when parameters are unused it is always better to remove them. Remove ``notify_by_email`` parameter in ``_notify_thread``. It is only used in channels to avoid notifying people of some automated notifications. The same behavior has been cleanly implemented at odoo/odoo@018820d . Rename methods, starting with ``_notify(_records)`` to ease their grouping and understanding. Add some additional prefixes like ``_notify_by_'mean'`` and ``_notify_get_recipients`` for recipients related computation. Add some docstrings, notably when parameters usage is not clear. Some linting is also performed in updated actions, just to lessen styling issues notably on runbot. This commit should not change anything functionally as it contains only some renaming and docstrings updates as well as some outdated parameters removal. Task-2710804 (Mail: Clean Mail.Thread API) Part-of: odoo/odoo#82167
This commit is contained in:
@@ -1751,10 +1751,10 @@ class Lead(models.Model):
|
||||
return self.env.ref('crm.mt_lead_lost')
|
||||
return super(Lead, self)._track_subtype(init_values)
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
""" Handle salesman recipients that can convert leads into opportunities
|
||||
and set opportunities as won / lost. """
|
||||
groups = super(Lead, self)._notify_get_groups(msg_vals=msg_vals)
|
||||
groups = super(Lead, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
local_msg_vals = dict(msg_vals or {})
|
||||
|
||||
self.ensure_one()
|
||||
@@ -1777,19 +1777,20 @@ class Lead(models.Model):
|
||||
|
||||
salesman_group_id = self.env.ref('sales_team.group_sale_salesman').id
|
||||
new_group = (
|
||||
'group_sale_salesman', lambda pdata: pdata['type'] == 'user' and salesman_group_id in pdata['groups'], {
|
||||
'actions': salesman_actions,
|
||||
})
|
||||
'group_sale_salesman',
|
||||
lambda pdata: pdata['type'] == 'user' and salesman_group_id in pdata['groups'],
|
||||
{'actions': salesman_actions}
|
||||
)
|
||||
|
||||
return [new_group] + groups
|
||||
|
||||
def _notify_get_reply_to(self, default=None, records=None, company=None, doc_names=None):
|
||||
def _notify_get_reply_to(self, default=None):
|
||||
""" Override to set alias of lead and opportunities to their sales team if any. """
|
||||
aliases = self.mapped('team_id').sudo()._notify_get_reply_to(default=default, records=None, company=company, doc_names=None)
|
||||
aliases = self.mapped('team_id').sudo()._notify_get_reply_to(default=default)
|
||||
res = {lead.id: aliases.get(lead.team_id.id) for lead in self}
|
||||
leftover = self.filtered(lambda rec: not rec.team_id)
|
||||
if leftover:
|
||||
res.update(super(Lead, leftover)._notify_get_reply_to(default=default, records=None, company=company, doc_names=doc_names))
|
||||
res.update(super(Lead, leftover)._notify_get_reply_to(default=default))
|
||||
return res
|
||||
|
||||
def _message_get_default_recipients(self):
|
||||
|
||||
@@ -1430,10 +1430,10 @@ class HolidaysRequest(models.Model):
|
||||
return leave_notif_subtype or self.env.ref('hr_holidays.mt_leave')
|
||||
return super(HolidaysRequest, self)._track_subtype(init_values)
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
""" Handle HR users and officers recipients that can validate or refuse holidays
|
||||
directly from email. """
|
||||
groups = super(HolidaysRequest, self)._notify_get_groups(msg_vals=msg_vals)
|
||||
groups = super(HolidaysRequest, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
local_msg_vals = dict(msg_vals or {})
|
||||
|
||||
self.ensure_one()
|
||||
@@ -1447,9 +1447,10 @@ class HolidaysRequest(models.Model):
|
||||
|
||||
holiday_user_group_id = self.env.ref('hr_holidays.group_hr_holidays_user').id
|
||||
new_group = (
|
||||
'group_hr_holidays_user', lambda pdata: pdata['type'] == 'user' and holiday_user_group_id in pdata['groups'], {
|
||||
'actions': hr_actions,
|
||||
})
|
||||
'group_hr_holidays_user',
|
||||
lambda pdata: pdata['type'] == 'user' and holiday_user_group_id in pdata['groups'],
|
||||
{'actions': hr_actions}
|
||||
)
|
||||
|
||||
return [new_group] + groups
|
||||
|
||||
|
||||
@@ -743,10 +743,10 @@ class HolidaysAllocation(models.Model):
|
||||
return allocation_notif_subtype_id or self.env.ref('hr_holidays.mt_leave_allocation')
|
||||
return super(HolidaysAllocation, self)._track_subtype(init_values)
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
""" Handle HR users and officers recipients that can validate or refuse holidays
|
||||
directly from email. """
|
||||
groups = super(HolidaysAllocation, self)._notify_get_groups(msg_vals=msg_vals)
|
||||
groups = super(HolidaysAllocation, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
local_msg_vals = dict(msg_vals or {})
|
||||
|
||||
self.ensure_one()
|
||||
@@ -760,9 +760,10 @@ class HolidaysAllocation(models.Model):
|
||||
|
||||
holiday_user_group_id = self.env.ref('hr_holidays.group_hr_holidays_user').id
|
||||
new_group = (
|
||||
'group_hr_holidays_user', lambda pdata: pdata['type'] == 'user' and holiday_user_group_id in pdata['groups'], {
|
||||
'actions': hr_actions,
|
||||
})
|
||||
'group_hr_holidays_user',
|
||||
lambda pdata: pdata['type'] == 'user' and holiday_user_group_id in pdata['groups'],
|
||||
{'actions': hr_actions}
|
||||
)
|
||||
|
||||
return [new_group] + groups
|
||||
|
||||
|
||||
@@ -500,13 +500,13 @@ class Applicant(models.Model):
|
||||
return self.env.ref('hr_recruitment.mt_applicant_stage_changed')
|
||||
return super(Applicant, self)._track_subtype(init_values)
|
||||
|
||||
def _notify_get_reply_to(self, default=None, records=None, company=None, doc_names=None):
|
||||
def _notify_get_reply_to(self, default=None):
|
||||
""" Override to set alias of applicants to their job definition if any. """
|
||||
aliases = self.mapped('job_id')._notify_get_reply_to(default=default, records=None, company=company, doc_names=None)
|
||||
aliases = self.mapped('job_id')._notify_get_reply_to(default=default)
|
||||
res = {app.id: aliases.get(app.job_id.id) for app in self}
|
||||
leftover = self.filtered(lambda rec: not rec.job_id)
|
||||
if leftover:
|
||||
res.update(super(Applicant, leftover)._notify_get_reply_to(default=default, records=None, company=company, doc_names=doc_names))
|
||||
res.update(super(Applicant, leftover)._notify_get_reply_to(default=default))
|
||||
return res
|
||||
|
||||
def _message_get_suggested_recipients(self):
|
||||
|
||||
@@ -349,14 +349,14 @@ class Channel(models.Model):
|
||||
new_partner_id=channel_partner.partner_id.id,
|
||||
new_partner_name=channel_partner.partner_id.name,
|
||||
)
|
||||
channel_partner.channel_id.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment", notify_by_email=False)
|
||||
channel_partner.channel_id.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment")
|
||||
members_data.append({
|
||||
'id': channel_partner.partner_id.id,
|
||||
'im_status': channel_partner.partner_id.im_status,
|
||||
'name': channel_partner.partner_id.name,
|
||||
})
|
||||
for channel_partner in new_members.filtered(lambda channel_partner: channel_partner.guest_id):
|
||||
channel_partner.channel_id.message_post(body=_('<div class="o_mail_notification">joined the channel</div>'), message_type="notification", subtype_xmlid="mail.mt_comment", notify_by_email=False)
|
||||
channel_partner.channel_id.message_post(body=_('<div class="o_mail_notification">joined the channel</div>'), message_type="notification", subtype_xmlid="mail.mt_comment")
|
||||
guest_members_data.append({
|
||||
'id': channel_partner.guest_id.id,
|
||||
'name': channel_partner.guest_id.name,
|
||||
@@ -479,13 +479,13 @@ class Channel(models.Model):
|
||||
return False
|
||||
return super(Channel, self)._alias_get_error_message(message, message_dict, alias)
|
||||
|
||||
def _notify_compute_recipients(self, message, msg_vals):
|
||||
def _notify_get_recipients(self, message, msg_vals):
|
||||
""" Override recipients computation as channel is not a standard
|
||||
mail.thread document. Indeed there are no followers on a channel.
|
||||
Instead of followers it has members that should be notified.
|
||||
|
||||
:param message: see ``MailThread._notify_compute_recipients()``;
|
||||
:param msg_vals: see ``MailThread._notify_compute_recipients()``;
|
||||
:param message: see ``MailThread._notify_get_recipients()``;
|
||||
:param msg_vals: see ``MailThread._notify_get_recipients()``;
|
||||
|
||||
:return recipients: structured data holding recipients data. See
|
||||
``MailThread._notify_thread()`` for more details about its content
|
||||
@@ -536,13 +536,13 @@ class Channel(models.Model):
|
||||
|
||||
return recipients_data
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
""" All recipients of a message on a channel are considered as partners.
|
||||
This means they will receive a minimal email, without a link to access
|
||||
in the backend. Mailing lists should indeed send minimal emails to avoid
|
||||
the noise. """
|
||||
groups = super(Channel, self)._notify_get_groups(msg_vals=msg_vals)
|
||||
for (index, (group_name, group_func, group_data)) in enumerate(groups):
|
||||
groups = super(Channel, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
for (index, (group_name, _group_func, group_data)) in enumerate(groups):
|
||||
if group_name != 'customer':
|
||||
groups[index] = (group_name, lambda partner: False, group_data)
|
||||
return groups
|
||||
|
||||
+225
-166
@@ -1638,9 +1638,9 @@ class MailThread(models.AbstractModel):
|
||||
]).write({'author_id': partner.id})
|
||||
return result
|
||||
|
||||
# ------------------------------------------------------
|
||||
# ------------------------------------------------------------
|
||||
# MESSAGE POST MAIN
|
||||
# ------------------------------------------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _message_post_process_attachments(self, attachments, attachment_ids, message_values):
|
||||
""" Preprocess attachments for mail_thread.message_post() or mail_mail.create().
|
||||
@@ -1868,9 +1868,9 @@ class MailThread(models.AbstractModel):
|
||||
message and computed value are given, to try to lessen query count by
|
||||
using already-computed values instead of having to rebrowse things. """
|
||||
|
||||
# ------------------------------------------------------
|
||||
# ------------------------------------------------------------
|
||||
# MESSAGE POST API / WRAPPERS
|
||||
# ------------------------------------------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _message_compose_with_view(self, views_or_xmlid, message_log=False, **kwargs):
|
||||
""" Helper method to send a mail / post a message / log a note using
|
||||
@@ -1970,7 +1970,7 @@ class MailThread(models.AbstractModel):
|
||||
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||||
'is_internal': True,
|
||||
'record_name': False,
|
||||
'reply_to': MailThread._notify_get_reply_to(default=email_from, records=None)[False],
|
||||
'reply_to': MailThread._notify_get_reply_to(default=email_from)[False],
|
||||
'message_id': tools.generate_tracking_message_id('message-notify'),
|
||||
}
|
||||
values.update(msg_kwargs)
|
||||
@@ -2003,7 +2003,7 @@ class MailThread(models.AbstractModel):
|
||||
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||||
'is_internal': True,
|
||||
'record_name': False,
|
||||
'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from, records=None)[False],
|
||||
'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from)[False],
|
||||
'message_id': tools.generate_tracking_message_id('message-notify'), # why? this is all but a notify
|
||||
}
|
||||
message_values.update(kwargs)
|
||||
@@ -2026,7 +2026,7 @@ class MailThread(models.AbstractModel):
|
||||
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||||
'is_internal': True,
|
||||
'record_name': False,
|
||||
'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from, records=None)[False],
|
||||
'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from)[False],
|
||||
'message_id': tools.generate_tracking_message_id('message-notify'), # why? this is all but a notify
|
||||
}
|
||||
values_list = [dict(base_message_values,
|
||||
@@ -2035,6 +2035,10 @@ class MailThread(models.AbstractModel):
|
||||
for record in self]
|
||||
return self.sudo()._message_create(values_list)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# MAIL.MESSAGE HELPERS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _message_compute_author(self, author_id=None, email_from=None, raise_exception=True):
|
||||
""" Tool method computing author information for messages. Purpose is
|
||||
to ensure maximum coherence between author / current user / email_from
|
||||
@@ -2097,43 +2101,57 @@ class MailThread(models.AbstractModel):
|
||||
# NOTIFICATION API
|
||||
# ------------------------------------------------------
|
||||
|
||||
def _notify_thread(self, message, msg_vals=False, notify_by_email=True, **kwargs):
|
||||
def _notify_thread(self, message, msg_vals=False, **kwargs):
|
||||
""" Main notification method. This method basically does two things
|
||||
|
||||
* call ``_notify_compute_recipients`` that computes recipients to
|
||||
* call ``_notify_get_recipients`` that computes recipients to
|
||||
notify based on message record or message creation values if given
|
||||
(to optimize performance if we already have data computed);
|
||||
* performs the notification process by calling the various notification
|
||||
methods implemented;
|
||||
|
||||
:param message: mail.message record to notify;
|
||||
:param msg_vals: dictionary of values used to create the message. If given
|
||||
it is used instead of accessing ``self`` to lessen query count in some
|
||||
simple cases where no notification is actually required;
|
||||
:param message: ``mail.message`` record to notify;
|
||||
:param msg_vals: dictionary of values used to create the message. If given it
|
||||
may be used to access values related to ``message`` without accessing it
|
||||
directly. It lessens query count in some optimized use cases by avoiding
|
||||
access message content in db;
|
||||
|
||||
Kwargs allow to pass various parameters that are given to sub notification
|
||||
methods. See those methods for more details about the additional parameters.
|
||||
Parameters used for email-style notifications
|
||||
|
||||
:return: recipients data (see ``MailThread._notify_get_recipients()``)
|
||||
"""
|
||||
msg_vals = msg_vals if msg_vals else {}
|
||||
rdata = self._notify_compute_recipients(message, msg_vals)
|
||||
if not rdata:
|
||||
return rdata
|
||||
recipients_data = self._notify_get_recipients(message, msg_vals)
|
||||
if not recipients_data:
|
||||
return recipients_data
|
||||
|
||||
self._notify_record_by_inbox(message, rdata, msg_vals=msg_vals, **kwargs)
|
||||
if notify_by_email:
|
||||
self._notify_record_by_email(message, rdata, msg_vals=msg_vals, **kwargs)
|
||||
self._notify_thread_by_inbox(message, recipients_data, msg_vals=msg_vals, **kwargs)
|
||||
self._notify_thread_by_email(message, recipients_data, msg_vals=msg_vals, **kwargs)
|
||||
|
||||
return rdata
|
||||
return recipients_data
|
||||
|
||||
def _notify_record_by_inbox(self, message, recipients_data, msg_vals=False, **kwargs):
|
||||
""" Notification method: inbox. Do two main things
|
||||
def _notify_thread_by_inbox(self, message, recipients_data, msg_vals=False, **kwargs):
|
||||
""" Notification method: inbox. Does two main things :
|
||||
|
||||
* create an inbox notification for users;
|
||||
* create inbox notifications for users;
|
||||
* send bus notifications;
|
||||
|
||||
TDE/XDO TODO: flag rdata directly, with for example r['notif'] = 'ocn_client' and r['needaction']=False
|
||||
and correctly override notify_recipients
|
||||
:param message: ``mail.message`` record to notify;
|
||||
:param recipients_data: list of recipients information (based on res.partner
|
||||
records), formatted like
|
||||
[{'active': partner.active;
|
||||
'id': id of the res.partner being recipient to notify;
|
||||
'groups': res.group IDs if linked to a user;
|
||||
'notif': 'inbox', 'email', 'sms' (SMS App);
|
||||
'share': partner.partner_share;
|
||||
'type': 'customer', 'portal', 'user;'
|
||||
}, {...}].
|
||||
See ``MailThread._notify_get_recipients``;
|
||||
:param msg_vals: dictionary of values used to create the message. If given it
|
||||
may be used to access values related to ``message`` without accessing it
|
||||
directly. It lessens query count in some optimized use cases by avoiding
|
||||
access message content in db;
|
||||
"""
|
||||
bus_notifications = []
|
||||
inbox_pids = [r['id'] for r in recipients_data if r['notif'] == 'inbox']
|
||||
@@ -2151,19 +2169,34 @@ class MailThread(models.AbstractModel):
|
||||
bus_notifications.append((self.env['res.partner'].browse(partner_id), 'mail.message/inbox', dict(message_format_values)))
|
||||
self.env['bus.bus'].sudo()._sendmany(bus_notifications)
|
||||
|
||||
def _notify_record_by_email(self, message, recipients_data, msg_vals=False,
|
||||
model_description=False, mail_auto_delete=True, check_existing=False,
|
||||
force_send=True, send_after_commit=True,
|
||||
def _notify_thread_by_email(self, message, recipients_data, msg_vals=False,
|
||||
mail_auto_delete=True, # mail.mail
|
||||
model_description=False, # rendering
|
||||
check_existing=False, force_send=True, send_after_commit=True, # email send
|
||||
**kwargs):
|
||||
""" Method to send email linked to notified messages.
|
||||
|
||||
:param message: mail.message record to notify;
|
||||
:param recipients_data: see ``_notify_thread``;
|
||||
:param msg_vals: see ``_notify_thread``;
|
||||
:param message: ``mail.message`` record to notify;
|
||||
:param recipients_data: list of recipients information (based on res.partner
|
||||
records), formatted like
|
||||
[{'active': partner.active;
|
||||
'id': id of the res.partner being recipient to notify;
|
||||
'groups': res.group IDs if linked to a user;
|
||||
'notif': 'inbox', 'email', 'sms' (SMS App);
|
||||
'share': partner.partner_share;
|
||||
'type': 'customer', 'portal', 'user;'
|
||||
}, {...}].
|
||||
See ``MailThread._notify_get_recipients``;
|
||||
:param msg_vals: dictionary of values used to create the message. If given it
|
||||
may be used to access values related to ``message`` without accessing it
|
||||
directly. It lessens query count in some optimized use cases by avoiding
|
||||
access message content in db;
|
||||
|
||||
:param mail_auto_delete: delete notification emails once sent;
|
||||
|
||||
:param model_description: model description used in email notification process
|
||||
(computed if not given);
|
||||
:param mail_auto_delete: delete notification emails once sent;
|
||||
|
||||
:param check_existing: check for existing notifications to update based on
|
||||
mailed recipient, otherwise create new notifications;
|
||||
|
||||
@@ -2177,13 +2210,13 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
model = msg_vals.get('model') if msg_vals else message.model
|
||||
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, msg_vals=msg_vals)
|
||||
recipients_groups_data = self._notify_get_recipients_classify(partners_data, model_name, msg_vals=msg_vals)
|
||||
|
||||
if not recipients_groups_data:
|
||||
return True
|
||||
force_send = self.env.context.get('mail_notify_force_send', force_send)
|
||||
|
||||
template_values = self._notify_prepare_template_context(message, msg_vals, model_description=model_description) # 10 queries
|
||||
template_values = self._notify_by_email_prepare_rendering_context(message, msg_vals, model_description=model_description) # 10 queries
|
||||
|
||||
email_layout_xmlid = msg_vals.get('email_layout_xmlid') if msg_vals else message.email_layout_xmlid
|
||||
template_xmlid = email_layout_xmlid if email_layout_xmlid else 'mail.message_notification_email'
|
||||
@@ -2205,7 +2238,7 @@ class MailThread(models.AbstractModel):
|
||||
'references': message.parent_id.sudo().message_id if message.parent_id else False,
|
||||
'subject': mail_subject,
|
||||
}
|
||||
base_mail_values = self._notify_by_email_add_values(base_mail_values)
|
||||
base_mail_values = self._notify_by_email_add_mail_values(base_mail_values)
|
||||
|
||||
# Clean the context to get rid of residual default_* keys that could cause issues during
|
||||
# the mail.mail creation.
|
||||
@@ -2235,7 +2268,7 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
# create email
|
||||
for recipients_ids_chunk in split_every(recipients_max, recipients_ids):
|
||||
recipient_values = self._notify_email_recipient_values(recipients_ids_chunk)
|
||||
recipient_values = self._notify_by_email_get_recipients_values(recipients_ids_chunk)
|
||||
email_to = recipient_values['email_to']
|
||||
recipient_ids = recipient_values['recipient_ids']
|
||||
|
||||
@@ -2302,8 +2335,25 @@ class MailThread(models.AbstractModel):
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def _notify_prepare_template_context(self, message, msg_vals, model_description=False, mail_auto_delete=True):
|
||||
# compute send user and its related signature
|
||||
def _notify_by_email_prepare_rendering_context(self, message, msg_vals, model_description=False):
|
||||
""" Prepare rendering context for notification email.
|
||||
|
||||
Signature: if asked a default signature is computed based on author. Either
|
||||
it has an user and we use the user's signature. Either we do not find any
|
||||
user and we compute a default one based on the author's name.
|
||||
|
||||
Company: either there is one defined on the record (company_id field set
|
||||
with a value), either we use env.company.
|
||||
|
||||
Lang: when calling this method, ``_fallback_lang`` should already been
|
||||
called, or a lang set in context with another way. A wild guess is done
|
||||
based on templates to try to retrieve the recipient's language when a flow
|
||||
like "send by email" is performed. Lang is used to try to have the
|
||||
notification layout in the same language as the email content.
|
||||
|
||||
:param model_description: model description used in email notification process
|
||||
(computed if not given);
|
||||
"""
|
||||
signature = ''
|
||||
user = self.env.user
|
||||
author = message.env['res.partner'].browse(msg_vals.get('author_id')) if msg_vals else message.author_id
|
||||
@@ -2323,17 +2373,12 @@ class MailThread(models.AbstractModel):
|
||||
if add_sign:
|
||||
signature = "<p>-- <br/>%s</p>" % author.name
|
||||
|
||||
# company value should fall back on env.company if:
|
||||
# - no company_id field on record
|
||||
# - company_id field available but not set
|
||||
company = self.company_id.sudo() if self and 'company_id' in self and self.company_id else self.env.company
|
||||
if company.website:
|
||||
website_url = 'http://%s' % company.website if not company.website.lower().startswith(('http:', 'https:')) else company.website
|
||||
else:
|
||||
website_url = False
|
||||
|
||||
# Retrieve the language in which the template was rendered, in order to render the custom
|
||||
# layout in the same language.
|
||||
# TDE FIXME: this whole brol should be cleaned !
|
||||
lang = self.env.context.get('lang')
|
||||
if {'default_template_id', 'default_model', 'default_res_id'} <= self.env.context.keys():
|
||||
@@ -2369,7 +2414,7 @@ class MailThread(models.AbstractModel):
|
||||
'lang': lang,
|
||||
}
|
||||
|
||||
def _notify_by_email_add_values(self, base_mail_values):
|
||||
def _notify_by_email_add_mail_values(self, base_mail_values):
|
||||
""" Add model-specific values to the dictionary used to create the
|
||||
notification email. Its base behavior is to compute model-specific
|
||||
headers.
|
||||
@@ -2377,14 +2422,40 @@ class MailThread(models.AbstractModel):
|
||||
:param dict base_mail_values: base mail.mail values, holding message
|
||||
to notify (mail_message_id and its fields), server, references, subject.
|
||||
"""
|
||||
headers = self._notify_email_headers()
|
||||
headers = self._notify_by_email_get_headers()
|
||||
if headers:
|
||||
base_mail_values['headers'] = headers
|
||||
base_mail_values['headers'] = repr(headers)
|
||||
return base_mail_values
|
||||
|
||||
def _notify_compute_recipients(self, message, msg_vals):
|
||||
def _notify_by_email_get_recipients_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
|
||||
recipients. Override to generate other mail values like email_to or
|
||||
email_cc.
|
||||
:param recipient_ids: res.partner recordset to notify
|
||||
"""
|
||||
return {
|
||||
'email_to': False,
|
||||
'recipient_ids': recipient_ids,
|
||||
}
|
||||
|
||||
def _notify_get_recipients(self, message, msg_vals):
|
||||
""" Compute recipients to notify based on subtype and followers. This
|
||||
method returns data structured as expected for ``_notify_recipients``. """
|
||||
method returns data structured as expected for ``_notify_recipients``.
|
||||
|
||||
TDE/XDO TODO: flag rdata directly, with for example r['notif'] = 'ocn_client' and r['needaction']=False
|
||||
and correctly override _notify_get_recipients
|
||||
|
||||
:return list recipients_data: this is a list of recipients information
|
||||
composed of a dictionary {
|
||||
'active': partner.active;
|
||||
'id': id of the res.partner;
|
||||
'groups': res.group IDs if linked to a user;
|
||||
'notif': 'inbox', 'email', 'sms' (SMS App);
|
||||
'share': partner.partner_share;
|
||||
'type': 'customer', 'portal', 'user;'
|
||||
}
|
||||
"""
|
||||
msg_sudo = message.sudo()
|
||||
# get values from msg_vals or from message if msg_vals doen't exists
|
||||
pids = msg_vals.get('partner_ids', []) if msg_vals else msg_sudo.partner_ids.ids
|
||||
@@ -2416,6 +2487,111 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
return recipients_data
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
""" Return groups used to classify recipients of a notification email.
|
||||
Groups is a list of tuple containing of form (group_name, group_func,
|
||||
group_data) where
|
||||
* group_name is an identifier used only to be able to override and manipulate
|
||||
groups. Default groups are user (recipients linked to an employee user),
|
||||
portal (recipients linked to a portal user) and customer (recipients not
|
||||
linked to any user). An example of override use would be to add a group
|
||||
linked to a res.groups like Hr Officers to set specific action buttons to
|
||||
them.
|
||||
* group_func is a function pointer taking a partner record as parameter. This
|
||||
method will be applied on recipients to know whether they belong to a given
|
||||
group or not. Only first matching group is kept. Evaluation order is the
|
||||
list order.
|
||||
* group_data is a dict containing parameters for the notification email
|
||||
* has_button_access: whether to display Access <Document> in email. True
|
||||
by default for new groups, False for portal / customer.
|
||||
* button_access: dict with url and title of the button
|
||||
* actions: list of action buttons to display in the notification email.
|
||||
Each action is a dict containing url and title of the button.
|
||||
Groups has a default value that you can find in mail_thread
|
||||
``_notify_get_recipients_classify`` method.
|
||||
"""
|
||||
return [
|
||||
(
|
||||
'user',
|
||||
lambda pdata: pdata['type'] == 'user',
|
||||
{'has_button_access': True}
|
||||
), (
|
||||
'portal',
|
||||
lambda pdata: pdata['type'] == 'portal',
|
||||
{'has_button_access': False}
|
||||
), (
|
||||
'customer',
|
||||
lambda pdata: True,
|
||||
{'has_button_access': False}
|
||||
)
|
||||
]
|
||||
|
||||
def _notify_get_recipients_classify(self, recipient_data, model_name, msg_vals=None):
|
||||
""" Classify recipients to be notified of a message in groups to have
|
||||
specific rendering depending on their group. For example users could
|
||||
have access to buttons customers should not have in their emails.
|
||||
Module-specific grouping should be done by overriding ``_notify_get_recipients_groups``
|
||||
method defined here-under.
|
||||
:param recipient_data:todo xdo UPDATE ME
|
||||
return example:
|
||||
[{
|
||||
'actions': [],
|
||||
'button_access': {'title': 'View Simple Chatter Model',
|
||||
'url': '/mail/view?model=mail.test.simple&res_id=1497'},
|
||||
'has_button_access': False,
|
||||
'recipients': [11]
|
||||
},
|
||||
{
|
||||
'actions': [],
|
||||
'button_access': {'title': 'View Simple Chatter Model',
|
||||
'url': '/mail/view?model=mail.test.simple&res_id=1497'},
|
||||
'has_button_access': False,
|
||||
'recipients': [4, 5, 6]
|
||||
},
|
||||
{
|
||||
'actions': [],
|
||||
'button_access': {'title': 'View Simple Chatter Model',
|
||||
'url': '/mail/view?model=mail.test.simple&res_id=1497'},
|
||||
'has_button_access': True,
|
||||
'recipients': [10, 11, 12]
|
||||
}]
|
||||
only return groups with recipients
|
||||
"""
|
||||
# keep a local copy of msg_vals as it may be modified to include more information about groups or links
|
||||
local_msg_vals = dict(msg_vals) if msg_vals else {}
|
||||
groups = self._notify_get_recipients_groups(msg_vals=local_msg_vals)
|
||||
access_link = self._notify_get_action_link('view', **local_msg_vals)
|
||||
|
||||
if model_name:
|
||||
view_title = _('View %s', model_name)
|
||||
else:
|
||||
view_title = _('View')
|
||||
|
||||
# fill group_data with default_values if they are not complete
|
||||
for group_name, group_func, group_data in groups:
|
||||
group_data.setdefault('notification_group_name', group_name)
|
||||
group_data.setdefault('notification_is_customer', False)
|
||||
group_data.setdefault('has_button_access', True)
|
||||
group_button_access = group_data.setdefault('button_access', {})
|
||||
group_button_access.setdefault('url', access_link)
|
||||
group_button_access.setdefault('title', view_title)
|
||||
group_data.setdefault('actions', list())
|
||||
group_data.setdefault('recipients', list())
|
||||
|
||||
# classify recipients in each group
|
||||
for recipient in recipient_data:
|
||||
for group_name, group_func, group_data in groups:
|
||||
if group_func(recipient):
|
||||
group_data['recipients'].append(recipient['id'])
|
||||
break
|
||||
|
||||
result = []
|
||||
for group_name, _group_method, group_data in groups:
|
||||
if group_data['recipients']:
|
||||
result.append(group_data)
|
||||
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def _notify_encode_link(self, base_link, params):
|
||||
secret = self.env['ir.config_parameter'].sudo().get_param('database.secret')
|
||||
@@ -2456,123 +2632,6 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
return link
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
""" Return groups used to classify recipients of a notification email.
|
||||
Groups is a list of tuple containing of form (group_name, group_func,
|
||||
group_data) where
|
||||
* group_name is an identifier used only to be able to override and manipulate
|
||||
groups. Default groups are user (recipients linked to an employee user),
|
||||
portal (recipients linked to a portal user) and customer (recipients not
|
||||
linked to any user). An example of override use would be to add a group
|
||||
linked to a res.groups like Hr Officers to set specific action buttons to
|
||||
them.
|
||||
* group_func is a function pointer taking a partner record as parameter. This
|
||||
method will be applied on recipients to know whether they belong to a given
|
||||
group or not. Only first matching group is kept. Evaluation order is the
|
||||
list order.
|
||||
* group_data is a dict containing parameters for the notification email
|
||||
* has_button_access: whether to display Access <Document> in email. True
|
||||
by default for new groups, False for portal / customer.
|
||||
* button_access: dict with url and title of the button
|
||||
* actions: list of action buttons to display in the notification email.
|
||||
Each action is a dict containing url and title of the button.
|
||||
Groups has a default value that you can find in mail_thread
|
||||
``_notify_classify_recipients`` method.
|
||||
"""
|
||||
return [
|
||||
(
|
||||
'user',
|
||||
lambda pdata: pdata['type'] == 'user',
|
||||
{'has_button_access': True}
|
||||
), (
|
||||
'portal',
|
||||
lambda pdata: pdata['type'] == 'portal',
|
||||
{'has_button_access': False}
|
||||
), (
|
||||
'customer',
|
||||
lambda pdata: True,
|
||||
{'has_button_access': False}
|
||||
)
|
||||
]
|
||||
|
||||
def _notify_classify_recipients(self, recipient_data, model_name, msg_vals=None):
|
||||
""" Classify recipients to be notified of a message in groups to have
|
||||
specific rendering depending on their group. For example users could
|
||||
have access to buttons customers should not have in their emails.
|
||||
Module-specific grouping should be done by overriding ``_notify_get_groups``
|
||||
method defined here-under.
|
||||
:param recipient_data:todo xdo UPDATE ME
|
||||
return example:
|
||||
[{
|
||||
'actions': [],
|
||||
'button_access': {'title': 'View Simple Chatter Model',
|
||||
'url': '/mail/view?model=mail.test.simple&res_id=1497'},
|
||||
'has_button_access': False,
|
||||
'recipients': [11]
|
||||
},
|
||||
{
|
||||
'actions': [],
|
||||
'button_access': {'title': 'View Simple Chatter Model',
|
||||
'url': '/mail/view?model=mail.test.simple&res_id=1497'},
|
||||
'has_button_access': False,
|
||||
'recipients': [4, 5, 6]
|
||||
},
|
||||
{
|
||||
'actions': [],
|
||||
'button_access': {'title': 'View Simple Chatter Model',
|
||||
'url': '/mail/view?model=mail.test.simple&res_id=1497'},
|
||||
'has_button_access': True,
|
||||
'recipients': [10, 11, 12]
|
||||
}]
|
||||
only return groups with recipients
|
||||
"""
|
||||
# keep a local copy of msg_vals as it may be modified to include more information about groups or links
|
||||
local_msg_vals = dict(msg_vals) if msg_vals else {}
|
||||
groups = self._notify_get_groups(msg_vals=local_msg_vals)
|
||||
access_link = self._notify_get_action_link('view', **local_msg_vals)
|
||||
|
||||
if model_name:
|
||||
view_title = _('View %s', model_name)
|
||||
else:
|
||||
view_title = _('View')
|
||||
|
||||
# fill group_data with default_values if they are not complete
|
||||
for group_name, group_func, group_data in groups:
|
||||
group_data.setdefault('notification_group_name', group_name)
|
||||
group_data.setdefault('notification_is_customer', False)
|
||||
group_data.setdefault('has_button_access', True)
|
||||
group_button_access = group_data.setdefault('button_access', {})
|
||||
group_button_access.setdefault('url', access_link)
|
||||
group_button_access.setdefault('title', view_title)
|
||||
group_data.setdefault('actions', list())
|
||||
group_data.setdefault('recipients', list())
|
||||
|
||||
# classify recipients in each group
|
||||
for recipient in recipient_data:
|
||||
for group_name, group_func, group_data in groups:
|
||||
if group_func(recipient):
|
||||
group_data['recipients'].append(recipient['id'])
|
||||
break
|
||||
|
||||
result = []
|
||||
for group_name, group_method, group_data in groups:
|
||||
if group_data['recipients']:
|
||||
result.append(group_data)
|
||||
|
||||
return result
|
||||
|
||||
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
|
||||
recipients. Override to generate other mail values like email_to or
|
||||
email_cc.
|
||||
:param recipient_ids: res.partner recordset to notify
|
||||
"""
|
||||
return {
|
||||
'email_to': False,
|
||||
'recipient_ids': recipient_ids,
|
||||
}
|
||||
|
||||
# ------------------------------------------------------
|
||||
# FOLLOWERS API
|
||||
# ------------------------------------------------------
|
||||
|
||||
@@ -84,7 +84,7 @@ class BaseModel(models.AbstractModel):
|
||||
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):
|
||||
def _notify_get_reply_to(self, default=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
|
||||
@@ -104,19 +104,9 @@ class BaseModel(models.AbstractModel):
|
||||
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 <reply_to@email>"/
|
||||
"""
|
||||
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 []
|
||||
@@ -125,7 +115,7 @@ class BaseModel(models.AbstractModel):
|
||||
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()
|
||||
doc_names = dict()
|
||||
|
||||
if alias_domain:
|
||||
if model and res_ids:
|
||||
@@ -148,7 +138,7 @@ class BaseModel(models.AbstractModel):
|
||||
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
|
||||
company_name = 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]))
|
||||
@@ -203,16 +193,11 @@ class BaseModel(models.AbstractModel):
|
||||
'&', ('hidden', '=', False),
|
||||
'|', ('res_model', '=', self._name), ('res_model', '=', False)])
|
||||
|
||||
def _notify_email_headers(self):
|
||||
"""
|
||||
Generate the email headers based on record
|
||||
"""
|
||||
def _notify_by_email_get_headers(self):
|
||||
""" Generate the email headers based on record """
|
||||
if not self:
|
||||
return {}
|
||||
self.ensure_one()
|
||||
return repr(self._notify_email_header_dict())
|
||||
|
||||
def _notify_email_header_dict(self):
|
||||
return {
|
||||
'X-Odoo-Objects': "%s-%s" % (self._name, self.id),
|
||||
}
|
||||
|
||||
@@ -607,7 +607,7 @@ class MailCase(MockEmail):
|
||||
some notification methods, notably testing links or group-based notification
|
||||
details.
|
||||
|
||||
See notably ``MailThread._notify_compute_recipients()``.
|
||||
See notably ``MailThread._notify_get_recipients()``.
|
||||
"""
|
||||
return [
|
||||
{'id': partner.id,
|
||||
|
||||
@@ -252,7 +252,7 @@ class MailComposer(models.TransientModel):
|
||||
# 'purchase.order' which is used for a RFQ and and PO. To avoid confusion, we must use a
|
||||
# different wording depending on the state of the object.
|
||||
# Therefore, we can set the description in the context from the beginning to avoid falling
|
||||
# back on the regular display_name retrieved in '_notify_prepare_template_context'.
|
||||
# back on the regular display_name retrieved in ``_notify_by_email_prepare_rendering_context()``.
|
||||
model_description = self._context.get('model_description')
|
||||
|
||||
for wizard in self:
|
||||
@@ -395,7 +395,7 @@ class MailComposer(models.TransientModel):
|
||||
# mass mailing: rendering override wizard static values
|
||||
if mass_mail_mode and self.model:
|
||||
record = self.env[self.model].browse(res_id)
|
||||
mail_values['headers'] = record._notify_email_headers()
|
||||
mail_values['headers'] = repr(record._notify_by_email_get_headers())
|
||||
# keep a copy unless specifically requested, reset record name (avoid browsing records)
|
||||
mail_values.update(is_notification=not self.auto_delete_message, model=self.model, res_id=res_id, record_name=False)
|
||||
# auto deletion of mail_mail
|
||||
|
||||
@@ -78,7 +78,11 @@ class MailResendMessage(models.TransientModel):
|
||||
else: # has no user, is therefore customer
|
||||
email_partners_data.append(dict(pdata, type='customer'))
|
||||
|
||||
record._notify_record_by_email(message, email_partners_data, check_existing=True, send_after_commit=False)
|
||||
record._notify_thread_by_email(
|
||||
message, email_partners_data,
|
||||
check_existing=True,
|
||||
send_after_commit=False
|
||||
)
|
||||
|
||||
self.mail_message_id._notify_message_notification_update()
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
@@ -71,9 +71,15 @@ class Invite(models.TransientModel):
|
||||
'add_sign': True,
|
||||
})
|
||||
partners_data = []
|
||||
recipient_data = self.env['mail.followers']._get_recipient_data(document, 'comment', False, pids=new_partners.ids)
|
||||
for pid, active, pshare, notif, groups in recipient_data:
|
||||
pdata = {'id': pid, 'share': pshare, 'active': active, 'notif': 'email', 'groups': groups or []}
|
||||
recipients_data = self.env['mail.followers']._get_recipient_data(document, 'comment', False, pids=new_partners.ids)
|
||||
for pid, active, pshare, notif, groups in recipients_data:
|
||||
pdata = {
|
||||
'active': active,
|
||||
'id': pid,
|
||||
'groups': groups or [],
|
||||
'notif': 'email',
|
||||
'share': pshare,
|
||||
}
|
||||
if not pshare and notif: # has an user and is not shared, is therefore user
|
||||
partners_data.append(dict(pdata, type='user'))
|
||||
elif pshare and notif: # has an user and is shared, is therefore portal
|
||||
@@ -81,7 +87,10 @@ class Invite(models.TransientModel):
|
||||
else: # has no user, is therefore customer
|
||||
partners_data.append(dict(pdata, type='customer'))
|
||||
|
||||
document._notify_record_by_email(message, partners_data, send_after_commit=False)
|
||||
document._notify_thread_by_email(
|
||||
message, partners_data,
|
||||
send_after_commit=False
|
||||
)
|
||||
# in case of failure, the web client must know the message was
|
||||
# deleted to discard the related failure notification
|
||||
self.env['bus.bus']._sendone(self.env.user.partner_id, 'mail.message/delete', {'message_ids': message.ids})
|
||||
|
||||
@@ -424,7 +424,7 @@ class MailGroup(models.Model):
|
||||
# SMTP headers related to the subscription
|
||||
email_url_encoded = urls.url_quote(email_member)
|
||||
headers = {
|
||||
** self._notify_email_header_dict(),
|
||||
** self._notify_by_email_get_headers(),
|
||||
'List-Archive': f'<{base_url}/groups/{slug(self)}>',
|
||||
'List-Subscribe': f'<{base_url}/groups?email={email_url_encoded}>',
|
||||
'List-Unsubscribe': f'<{base_url}/groups?unsubscribe&email={email_url_encoded}>',
|
||||
|
||||
@@ -60,9 +60,9 @@ class PortalMixin(models.AbstractModel):
|
||||
|
||||
return '%s?%s' % ('/mail/view' if redirect else self.access_url, url_encode(params))
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
access_token = self._portal_ensure_token()
|
||||
groups = super(PortalMixin, self)._notify_get_groups(msg_vals=msg_vals)
|
||||
groups = super(PortalMixin, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
local_msg_vals = dict(msg_vals or {})
|
||||
|
||||
if access_token and 'partner_id' in self._fields and self['partner_id']:
|
||||
|
||||
@@ -1951,12 +1951,12 @@ class Task(models.Model):
|
||||
res -= dependency_subtype
|
||||
return res
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
""" Handle project users and managers recipients that can assign
|
||||
tasks and create new one directly from notification emails. Also give
|
||||
access button to portal users and portal customers. If they are notified
|
||||
they should probably have access to the document. """
|
||||
groups = super(Task, self)._notify_get_groups(msg_vals=msg_vals)
|
||||
groups = super(Task, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
local_msg_vals = dict(msg_vals or {})
|
||||
self.ensure_one()
|
||||
|
||||
@@ -1983,13 +1983,13 @@ class Task(models.Model):
|
||||
|
||||
return groups
|
||||
|
||||
def _notify_get_reply_to(self, default=None, records=None, company=None, doc_names=None):
|
||||
def _notify_get_reply_to(self, default=None):
|
||||
""" Override to set alias of tasks to their project if any. """
|
||||
aliases = self.sudo().mapped('project_id')._notify_get_reply_to(default=default, records=None, company=company, doc_names=None)
|
||||
aliases = self.sudo().mapped('project_id')._notify_get_reply_to(default=default)
|
||||
res = {task.id: aliases.get(task.project_id.id) for task in self}
|
||||
leftover = self.filtered(lambda rec: not rec.project_id)
|
||||
if leftover:
|
||||
res.update(super(Task, leftover)._notify_get_reply_to(default=default, records=None, company=company, doc_names=doc_names))
|
||||
res.update(super(Task, leftover)._notify_get_reply_to(default=default))
|
||||
return res
|
||||
|
||||
def email_split(self, msg):
|
||||
@@ -2044,8 +2044,8 @@ class Task(models.Model):
|
||||
task._message_add_suggested_recipient(recipients, email=task.email_from, reason=_('Customer Email'))
|
||||
return recipients
|
||||
|
||||
def _notify_email_header_dict(self):
|
||||
headers = super(Task, self)._notify_email_header_dict()
|
||||
def _notify_by_email_get_headers(self):
|
||||
headers = super(Task, self)._notify_by_email_get_headers()
|
||||
if self.project_id:
|
||||
current_objects = [h for h in headers.get('X-Odoo-Objects', '').split(',') if h]
|
||||
current_objects.insert(0, 'project.project-%s, ' % self.project_id.id)
|
||||
|
||||
@@ -204,8 +204,8 @@ class MailThread(models.AbstractModel):
|
||||
:param partner_ids: if set is a record set of partners to notify;
|
||||
:param number_field: if set is a name of field to use on current record
|
||||
to compute a number to notify;
|
||||
:param sms_numbers: see ``_notify_record_by_sms``;
|
||||
:param sms_pid_to_number: see ``_notify_record_by_sms``;
|
||||
:param sms_numbers: see ``_notify_thread_by_sms``;
|
||||
:param sms_pid_to_number: see ``_notify_thread_by_sms``;
|
||||
"""
|
||||
self.ensure_one()
|
||||
sms_pid_to_number = sms_pid_to_number if sms_pid_to_number is not None else {}
|
||||
@@ -237,17 +237,29 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
def _notify_thread(self, message, msg_vals=False, **kwargs):
|
||||
recipients_data = super(MailThread, self)._notify_thread(message, msg_vals=msg_vals, **kwargs)
|
||||
self._notify_record_by_sms(message, recipients_data, msg_vals=msg_vals, **kwargs)
|
||||
self._notify_thread_by_sms(message, recipients_data, msg_vals=msg_vals, **kwargs)
|
||||
return recipients_data
|
||||
|
||||
def _notify_record_by_sms(self, message, recipients_data, msg_vals=False,
|
||||
def _notify_thread_by_sms(self, message, recipients_data, msg_vals=False,
|
||||
sms_numbers=None, sms_pid_to_number=None,
|
||||
check_existing=False, put_in_queue=False, **kwargs):
|
||||
""" Notification method: by SMS.
|
||||
|
||||
:param message: mail.message record to notify;
|
||||
:param recipients_data: see ``_notify_thread``;
|
||||
:param msg_vals: see ``_notify_thread``;
|
||||
:param message: ``mail.message`` record to notify;
|
||||
:param recipients_data: list of recipients information (based on res.partner
|
||||
records), formatted like
|
||||
[{'active': partner.active;
|
||||
'id': id of the res.partner being recipient to notify;
|
||||
'groups': res.group IDs if linked to a user;
|
||||
'notif': 'inbox', 'email', 'sms' (SMS App);
|
||||
'share': partner.partner_share;
|
||||
'type': 'customer', 'portal', 'user;'
|
||||
}, {...}].
|
||||
See ``MailThread._notify_get_recipients``;
|
||||
:param msg_vals: dictionary of values used to create the message. If given it
|
||||
may be used to access values related to ``message`` without accessing it
|
||||
directly. It lessens query count in some optimized use cases by avoiding
|
||||
access message content in db;
|
||||
|
||||
:param sms_numbers: additional numbers to notify in addition to partners
|
||||
and classic recipients;
|
||||
@@ -264,7 +276,7 @@ class MailThread(models.AbstractModel):
|
||||
sms_all = self.env['sms.sms'].sudo()
|
||||
|
||||
# pre-compute SMS data
|
||||
body = msg_vals['body'] if msg_vals and msg_vals.get('body') else message.body
|
||||
body = msg_vals['body'] if msg_vals and 'body' in msg_vals else message.body
|
||||
sms_base_vals = {
|
||||
'body': html2plaintext(body),
|
||||
'mail_message_id': message.id,
|
||||
|
||||
@@ -90,15 +90,22 @@ class SMSResend(models.TransientModel):
|
||||
pids = list(sms_pid_to_number.keys())
|
||||
numbers = [r.sms_number for r in self.recipient_ids if r.resend and not r.partner_id]
|
||||
|
||||
rdata = []
|
||||
recipients_data = []
|
||||
for pid, active, pshare, notif, groups in self.env['mail.followers']._get_recipient_data(record, 'sms', False, pids=pids):
|
||||
if pid and notif == 'sms':
|
||||
rdata.append({'id': pid, 'share': pshare, 'active': active, 'notif': notif, 'groups': groups or [], 'type': 'customer' if pshare else 'user'})
|
||||
if rdata or numbers:
|
||||
record._notify_record_by_sms(
|
||||
self.mail_message_id, rdata, check_existing=True,
|
||||
recipients_data.append({
|
||||
'active': active,
|
||||
'groups': groups or [],
|
||||
'id': pid,
|
||||
'notif': notif,
|
||||
'share': pshare,
|
||||
'type': 'customer' if pshare else 'user',
|
||||
})
|
||||
if recipients_data or numbers:
|
||||
record._notify_thread_by_sms(
|
||||
self.mail_message_id, recipients_data,
|
||||
sms_numbers=numbers, sms_pid_to_number=sms_pid_to_number,
|
||||
put_in_queue=False
|
||||
check_existing=True, put_in_queue=False
|
||||
)
|
||||
|
||||
self.mail_message_id._notify_message_notification_update()
|
||||
|
||||
@@ -55,7 +55,7 @@ class TestMessagePost(TestMailCommon, TestRecipients):
|
||||
'mail.mail_notification_paynow']:
|
||||
test_message.write({'email_layout_xmlid': email_xmlid})
|
||||
with self.mock_mail_gateway():
|
||||
test_record._notify_record_by_email(
|
||||
test_record._notify_thread_by_email(
|
||||
test_message,
|
||||
recipients_data,
|
||||
force_send=False
|
||||
@@ -71,7 +71,7 @@ class TestMessagePost(TestMailCommon, TestRecipients):
|
||||
self.assertTrue(user_email)
|
||||
|
||||
@users('employee')
|
||||
def test_notify_mail_add_signature(self):
|
||||
def test_notify_by_mail_add_signature(self):
|
||||
self.test_track = self.env['mail.test.track'].with_context(self._test_context).with_user(self.user_employee).create({
|
||||
'name': 'Test',
|
||||
'email_from': 'ignasse@example.com'
|
||||
@@ -96,7 +96,7 @@ class TestMessagePost(TestMailCommon, TestRecipients):
|
||||
self.assertEqual(found_mail.body_html.count(signature), 0)
|
||||
|
||||
@users('employee')
|
||||
def test_notify_prepare_template_context_company_value(self):
|
||||
def test_notify_by_email_prepare_rendering_contextt(self):
|
||||
""" Verify that the template context company value is right
|
||||
after switching the env company or if a company_id is set
|
||||
on mail record.
|
||||
@@ -113,7 +113,7 @@ class TestMessagePost(TestMailCommon, TestRecipients):
|
||||
# self.env.company.id = Main Company AND test_record.company_id = False
|
||||
self.assertEqual(self.env.company.id, main_company.id)
|
||||
self.assertEqual(test_record.company_id.id, False)
|
||||
template_values = test_record._notify_prepare_template_context(test_record.message_ids, {})
|
||||
template_values = test_record._notify_by_email_prepare_rendering_context(test_record.message_ids, {})
|
||||
self.assertEqual(template_values.get('company').id, self.env.company.id)
|
||||
|
||||
# self.env.company.id = Other Company AND test_record.company_id = False
|
||||
@@ -121,7 +121,7 @@ class TestMessagePost(TestMailCommon, TestRecipients):
|
||||
test_record = self.env['mail.test.multi.company'].browse(test_record.id)
|
||||
self.assertEqual(self.env.company.id, other_company.id)
|
||||
self.assertEqual(test_record.company_id.id, False)
|
||||
template_values = test_record._notify_prepare_template_context(test_record.message_ids, {})
|
||||
template_values = test_record._notify_by_email_prepare_rendering_context(test_record.message_ids, {})
|
||||
self.assertEqual(template_values.get('company').id, self.env.company.id)
|
||||
|
||||
# self.env.company.id = Other Company AND test_record.company_id = Main Company
|
||||
@@ -129,7 +129,7 @@ class TestMessagePost(TestMailCommon, TestRecipients):
|
||||
test_record = self.env['mail.test.multi.company'].browse(test_record.id)
|
||||
self.assertEqual(self.env.company.id, other_company.id)
|
||||
self.assertEqual(test_record.company_id.id, main_company.id)
|
||||
template_values = test_record._notify_prepare_template_context(test_record.message_ids, {})
|
||||
template_values = test_record._notify_by_email_prepare_rendering_context(test_record.message_ids, {})
|
||||
self.assertEqual(template_values.get('company').id, main_company.id)
|
||||
|
||||
def test_notify_recipients_internals(self):
|
||||
@@ -147,7 +147,7 @@ class TestMessagePost(TestMailCommon, TestRecipients):
|
||||
'auth_login': 'auth_login_val',
|
||||
}
|
||||
notify_msg_vals = dict(msg_vals, **link_vals)
|
||||
classify_res = self.env[self.test_record._name]._notify_classify_recipients(pdata, 'My Custom Model Name', msg_vals=notify_msg_vals)
|
||||
classify_res = self.env[self.test_record._name]._notify_get_recipients_classify(pdata, 'My Custom Model Name', msg_vals=notify_msg_vals)
|
||||
# find back information for each recipients
|
||||
partner_info = next(item for item in classify_res if item['recipients'] == self.partner_1.ids)
|
||||
emp_info = next(item for item in classify_res if item['recipients'] == self.partner_employee.ids)
|
||||
|
||||
@@ -349,8 +349,8 @@ class TestTrackingInternals(TestMailCommon):
|
||||
self.assertFalse(msg_emp[0].get('tracking_value_ids'), "should not have protected tracking values")
|
||||
self.assertTrue(msg_sudo[0].get('tracking_value_ids'), "should have protected tracking values")
|
||||
|
||||
msg_emp = self.record._notify_prepare_template_context(self.record.message_ids, {})
|
||||
msg_sudo = self.record.sudo()._notify_prepare_template_context(self.record.message_ids, {})
|
||||
msg_emp = self.record._notify_by_email_prepare_rendering_context(self.record.message_ids, {})
|
||||
msg_sudo = self.record.sudo()._notify_by_email_prepare_rendering_context(self.record.message_ids, {})
|
||||
self.assertFalse(msg_emp.get('tracking_values'), "should not have protected tracking values")
|
||||
self.assertTrue(msg_sudo.get('tracking_values'), "should have protected tracking values")
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class TestSMSPost(TestMailFullCommon, TestMailFullRecipients):
|
||||
|
||||
with self.with_user('employee'), self.mockSMSGateway():
|
||||
test_record = self.env['mail.test.sms'].browse(self.test_record.id)
|
||||
test_record._notify_record_by_sms(messages, [{'id': self.partner_1.id, 'notif': 'sms'}], check_existing=True)
|
||||
test_record._notify_thread_by_sms(messages, [{'id': self.partner_1.id, 'notif': 'sms'}], check_existing=True)
|
||||
self.assertSMSNotification([{'partner': self.partner_1}], self._test_body, messages)
|
||||
|
||||
def test_message_sms_internals_sms_numbers(self):
|
||||
|
||||
@@ -273,23 +273,25 @@ class BlogPost(models.Model):
|
||||
'res_id': self.id,
|
||||
}
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
""" Add access button to everyone if the document is published. """
|
||||
groups = super(BlogPost, self)._notify_get_groups(msg_vals=msg_vals)
|
||||
groups = super(BlogPost, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
|
||||
if self.website_published:
|
||||
for group_name, group_method, group_data in groups:
|
||||
for _group_name, _group_method, group_data in groups:
|
||||
group_data['has_button_access'] = True
|
||||
|
||||
return groups
|
||||
|
||||
def _notify_record_by_inbox(self, message, recipients_data, msg_vals=False, **kwargs):
|
||||
def _notify_thread_by_inbox(self, message, recipients_data, msg_vals=False, **kwargs):
|
||||
""" Override to avoid keeping all notified recipients of a comment.
|
||||
We avoid tracking needaction on post comments. Only emails should be
|
||||
sufficient. """
|
||||
if msg_vals is None:
|
||||
msg_vals = {}
|
||||
if msg_vals.get('message_type', message.message_type) == 'comment':
|
||||
return
|
||||
return super(BlogPost, self)._notify_record_by_inbox(message, recipients_data, msg_vals=msg_vals, **kwargs)
|
||||
return super(BlogPost, self)._notify_thread_by_inbox(message, recipients_data, msg_vals=msg_vals, **kwargs)
|
||||
|
||||
def _default_website_meta(self):
|
||||
res = super(BlogPost, self)._default_website_meta()
|
||||
|
||||
@@ -922,12 +922,12 @@ class Post(models.Model):
|
||||
'res_id': self.id,
|
||||
}
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
def _notify_recipients_get_groups(self, msg_vals=None):
|
||||
""" Add access button to everyone if the document is active. """
|
||||
groups = super(Post, self)._notify_get_groups(msg_vals=msg_vals)
|
||||
groups = super(Post, self)._notify_recipients_get_groups(msg_vals=msg_vals)
|
||||
|
||||
if self.state == 'active':
|
||||
for group_name, group_method, group_data in groups:
|
||||
for _group_name, _group_method, group_data in groups:
|
||||
group_data['has_button_access'] = True
|
||||
|
||||
return groups
|
||||
@@ -954,13 +954,15 @@ class Post(models.Model):
|
||||
kwargs['record_name'] = self.parent_id.name
|
||||
return super(Post, self).message_post(message_type=message_type, **kwargs)
|
||||
|
||||
def _notify_record_by_inbox(self, message, recipients_data, msg_vals=False, **kwargs):
|
||||
def _notify_thread_by_inbox(self, message, recipients_data, msg_vals=False, **kwargs):
|
||||
""" Override to avoid keeping all notified recipients of a comment.
|
||||
We avoid tracking needaction on post comments. Only emails should be
|
||||
sufficient. """
|
||||
if msg_vals is None:
|
||||
msg_vals = {}
|
||||
if msg_vals.get('message_type', message.message_type) == 'comment':
|
||||
return
|
||||
return super(Post, self)._notify_record_by_inbox(message, recipients_data, msg_vals=msg_vals, **kwargs)
|
||||
return super(Post, self)._notify_thread_by_inbox(message, recipients_data, msg_vals=msg_vals, **kwargs)
|
||||
|
||||
def _compute_website_url(self):
|
||||
return '/forum/{forum}/{post}{anchor}'.format(
|
||||
|
||||
@@ -662,12 +662,12 @@ class Slide(models.Model):
|
||||
}
|
||||
return super(Slide, self).get_access_action(access_uid)
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
""" Add access button to everyone if the document is active. """
|
||||
groups = super(Slide, self)._notify_get_groups(msg_vals=msg_vals)
|
||||
groups = super(Slide, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
|
||||
if self.website_published:
|
||||
for group_name, group_method, group_data in groups:
|
||||
for _group_name, _group_method, group_data in groups:
|
||||
group_data['has_button_access'] = True
|
||||
|
||||
return groups
|
||||
|
||||
@@ -89,7 +89,7 @@ class SlideChannelInvite(models.TransientModel):
|
||||
except ValueError:
|
||||
_logger.warning('QWeb template %s not found when sending slide channel mails. Sending without layout.', email_layout_xmlid)
|
||||
else:
|
||||
# could be great to use _notify_prepare_template_context someday
|
||||
# could be great to use ``_notify_by_email_prepare_rendering_context`` someday
|
||||
template_ctx = {
|
||||
'message': self.env['mail.message'].sudo().new(dict(body=mail_values['body_html'], record_name=self.channel_id.name)),
|
||||
'model_description': self.env['ir.model']._get('slide.channel').display_name,
|
||||
|
||||
Reference in New Issue
Block a user