[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:
Thibault Delavallée
2022-01-31 17:47:30 +00:00
parent 0b952df5e0
commit 1f7c83cc3e
23 changed files with 354 additions and 271 deletions
+9 -8
View File
@@ -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):
+6 -5
View File
@@ -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):
+8 -8
View File
@@ -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
View File
@@ -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
# ------------------------------------------------------
+5 -20
View File
@@ -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),
}
+1 -1
View File
@@ -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,
+2 -2
View File
@@ -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
+5 -1
View File
@@ -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'}
+13 -4
View File
@@ -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})
+1 -1
View File
@@ -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}>',
+2 -2
View File
@@ -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']:
+7 -7
View File
@@ -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)
+20 -8
View File
@@ -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,
+13 -6
View File
@@ -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()
+7 -7
View File
@@ -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)
+2 -2
View File
@@ -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")
+1 -1
View File
@@ -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):
+7 -5
View File
@@ -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()
+7 -5
View File
@@ -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(
+3 -3
View File
@@ -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,