[IMP] mail: cleanup _notify_get_recipients code bits
This commit contains mainly code cleaning, docstrings and a small split
for notification tool methods. In this commit we
* make some notification groups variable explicit;
* move the filler of groups into its own submethod to ease being called
from other code (to be used soon);
* fix some strange overrides or code manipulation;
* propagate some additional parameters to ease future commits that will
improve rendering of groups-based notification emails;
* cleanup, fixup and improve docstrings;
This does not change anything from functional point of view, just preparing
further work.
Task-3046371 (Mail: Better Language Support in Composer)
Part-of: odoo/odoo#106177
This commit is contained in:
@@ -1924,10 +1924,12 @@ class Lead(models.Model):
|
||||
_('Deadline: %s', self.date_deadline.strftime(get_lang(self.env).date_format)))
|
||||
return render_context
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" Handle salesman recipients that can convert leads into opportunities
|
||||
and set opportunities as won / lost. """
|
||||
groups = super(Lead, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
@@ -1955,7 +1957,11 @@ class Lead(models.Model):
|
||||
new_group = (
|
||||
'group_sale_salesman',
|
||||
lambda pdata: pdata['type'] == 'user' and salesman_group_id in pdata['groups'],
|
||||
{'actions': salesman_actions}
|
||||
{
|
||||
'actions': salesman_actions,
|
||||
'active': True,
|
||||
'has_button_access': True,
|
||||
}
|
||||
)
|
||||
|
||||
return [new_group] + groups
|
||||
|
||||
@@ -1648,10 +1648,12 @@ 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_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" Handle HR users and officers recipients that can validate or refuse holidays
|
||||
directly from email. """
|
||||
groups = super(HolidaysRequest, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
@@ -1670,7 +1672,11 @@ class HolidaysRequest(models.Model):
|
||||
new_group = (
|
||||
'group_hr_holidays_user',
|
||||
lambda pdata: pdata['type'] == 'user' and holiday_user_group_id in pdata['groups'],
|
||||
{'actions': hr_actions}
|
||||
{
|
||||
'actions': hr_actions,
|
||||
'active': True,
|
||||
'has_button_access': True,
|
||||
}
|
||||
)
|
||||
|
||||
return [new_group] + groups
|
||||
|
||||
@@ -793,10 +793,12 @@ 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_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" Handle HR users and officers recipients that can validate or refuse holidays
|
||||
directly from email. """
|
||||
groups = super(HolidaysAllocation, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
@@ -815,7 +817,11 @@ class HolidaysAllocation(models.Model):
|
||||
new_group = (
|
||||
'group_hr_holidays_user',
|
||||
lambda pdata: pdata['type'] == 'user' and holiday_user_group_id in pdata['groups'],
|
||||
{'actions': hr_actions}
|
||||
{
|
||||
'actions': hr_actions,
|
||||
'active': True,
|
||||
'has_button_access': True,
|
||||
}
|
||||
)
|
||||
|
||||
return [new_group] + groups
|
||||
|
||||
@@ -26,7 +26,7 @@ class MailController(http.Controller):
|
||||
base_link = request.httprequest.path
|
||||
params = dict(request.params)
|
||||
params.pop('token', '')
|
||||
valid_token = request.env['mail.thread']._notify_encode_link(base_link, params)
|
||||
valid_token = request.env['mail.thread']._encode_link(base_link, params)
|
||||
return consteq(valid_token, str(token))
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -545,12 +545,14 @@ class Channel(models.Model):
|
||||
|
||||
return recipients_data
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, 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_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, 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)
|
||||
|
||||
+280
-180
@@ -2879,11 +2879,10 @@ class MailThread(models.AbstractModel):
|
||||
* 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
|
||||
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 record message: <mail.message> record being notified. May be
|
||||
void as 'msg_vals' superseeds it;
|
||||
:param dict msg_vals: values dict used to create the message, allows to
|
||||
skip message usage and spare some queries;
|
||||
|
||||
Kwargs allow to pass various parameters that are given to sub notification
|
||||
methods. See those methods for more details about supported parameters.
|
||||
@@ -2927,26 +2926,28 @@ class MailThread(models.AbstractModel):
|
||||
return recipients_data
|
||||
|
||||
def _notify_thread_by_inbox(self, message, recipients_data, msg_vals=False, **kwargs):
|
||||
""" Notification method: inbox. Does two main things :
|
||||
""" Notificaty recipients inbox of a message. It does two main things :
|
||||
|
||||
* create inbox notifications for users;
|
||||
* send bus notifications;
|
||||
|
||||
: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 record message: <mail.message> record being notified. May be
|
||||
void as 'msg_vals' superseeds it;
|
||||
:param list recipients_data: list of recipients data based on <res.partner>
|
||||
records formatted like [
|
||||
{
|
||||
'active': partner.active;
|
||||
'id': id of the res.partner being recipient to notify;
|
||||
'is_follower': follows the message related document;
|
||||
'lang': its lang;
|
||||
'groups': res.group IDs if linked to a user;
|
||||
'notif': 'inbox', 'email', 'sms' (SMS App);
|
||||
'share': is partner a customer (partner.partner_share);
|
||||
'type': partner usage ('customer', 'portal', 'user');
|
||||
'ushare': are users shared (if users, all users are shared);
|
||||
}, {...}]. See ``MailThread._notify_get_recipients()``;
|
||||
:param dict msg_vals: values dict used to create the message, allows to
|
||||
skip message usage and spare some queries;
|
||||
"""
|
||||
bus_notifications = []
|
||||
inbox_pids = [r['id'] for r in recipients_data if r['notif'] == 'inbox']
|
||||
@@ -2968,39 +2969,44 @@ class MailThread(models.AbstractModel):
|
||||
def _notify_thread_by_email(self, message, recipients_data, msg_vals=False,
|
||||
mail_auto_delete=True, # mail.mail
|
||||
model_description=False, force_email_company=False, force_email_lang=False, # rendering
|
||||
subtitles=None, # rendering
|
||||
resend_existing=False, force_send=True, send_after_commit=True, # email send
|
||||
subtitles=None, **kwargs):
|
||||
""" Method to send email linked to notified messages.
|
||||
**kwargs):
|
||||
""" Method to send emails notifications linked to a message.
|
||||
|
||||
: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 record message: <mail.message> record being notified. May be
|
||||
void as 'msg_vals' superseeds it;
|
||||
:param list recipients_data: list of recipients data based on <res.partner>
|
||||
records formatted like [
|
||||
{
|
||||
'active': partner.active;
|
||||
'id': id of the res.partner being recipient to notify;
|
||||
'is_follower': follows the message related document;
|
||||
'lang': its lang;
|
||||
'groups': res.group IDs if linked to a user;
|
||||
'notif': 'inbox', 'email', 'sms' (SMS App);
|
||||
'share': is partner a customer (partner.partner_share);
|
||||
'type': partner usage ('customer', 'portal', 'user');
|
||||
'ushare': are users shared (if users, all users are shared);
|
||||
}, {...}]. See ``MailThread._notify_get_recipients()``;
|
||||
:param dict msg_vals: values dict used to create the message, allows to
|
||||
skip message usage and spare some queries;
|
||||
|
||||
:param mail_auto_delete: delete notification emails once sent;
|
||||
:param bool mail_auto_delete: delete notification emails once sent;
|
||||
|
||||
:param model_description: model description used in email notification process
|
||||
(computed if not given);
|
||||
:param force_email_company: see ``_notify_by_email_prepare_rendering_context``;
|
||||
:param force_email_lang: see ``_notify_by_email_prepare_rendering_context``;
|
||||
:param str model_description: description of current model, given to
|
||||
avoid fetching it and easing translation support;
|
||||
:param record force_email_company: <res.company> record used when rendering
|
||||
notification layout. Otherwise computed based on current record;
|
||||
:param str force_email_lang: lang used when rendering content, used
|
||||
notably to compute model name or translate access buttons;
|
||||
:param list subtitles: optional list set as template value "subtitles";
|
||||
|
||||
:param resend_existing: check for existing notifications to update based on
|
||||
mailed recipient, otherwise create new notifications;
|
||||
:param force_send: send emails directly instead of using queue;
|
||||
:param send_after_commit: if force_send, tells whether to send emails after
|
||||
:param bool resend_existing: check for existing notifications to update
|
||||
based on mailed recipient, otherwise create new notifications;
|
||||
:param bool force_send: send emails directly instead of using queue;
|
||||
:param bool send_after_commit: if force_send, tells to send emails after
|
||||
the transaction has been committed using a post-commit hook;
|
||||
:param subtitles: optional list that will be set as template value "subtitles"
|
||||
"""
|
||||
partners_data = [r for r in recipients_data if r['notif'] == 'email']
|
||||
if not partners_data:
|
||||
@@ -3008,9 +3014,11 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
model = msg_vals.get('model') if msg_vals else message.model
|
||||
model_name = model_description or (self.env['ir.model']._get(model).display_name if model else False) # one query for display name
|
||||
recipients_groups_data = self._notify_get_recipients_classify(partners_data, model_name, msg_vals=msg_vals)
|
||||
recipients_groups_list = self._notify_get_recipients_classify(
|
||||
message, partners_data, model_name, msg_vals=msg_vals
|
||||
)
|
||||
|
||||
if not recipients_groups_data:
|
||||
if not recipients_groups_list:
|
||||
return True
|
||||
force_send = self.env.context.get('mail_notify_force_send', force_send)
|
||||
|
||||
@@ -3039,10 +3047,10 @@ class MailThread(models.AbstractModel):
|
||||
# loop on groups (customer, portal, user, ... + model specific like group_sale_salesman)
|
||||
notif_create_values = []
|
||||
recipients_max = 50
|
||||
for recipients_group_data in recipients_groups_data:
|
||||
for recipients_group in recipients_groups_list:
|
||||
# generate notification email content
|
||||
recipients_ids = recipients_group_data.pop('recipients')
|
||||
render_values = {**template_values, **recipients_group_data}
|
||||
recipients_ids = recipients_group.pop('recipients')
|
||||
render_values = {**template_values, **recipients_group}
|
||||
# {company, is_discussion, lang, message, model_description, record, record_name, signature, subtype, tracking_values, website_url}
|
||||
# {actions, button_access, has_button_access, recipients}
|
||||
|
||||
@@ -3114,8 +3122,10 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
return True
|
||||
|
||||
def _notify_by_email_prepare_rendering_context(self, message, msg_vals=False, model_description=False,
|
||||
force_email_company=False, force_email_lang=False):
|
||||
def _notify_by_email_prepare_rendering_context(self, message, msg_vals=False,
|
||||
model_description=False,
|
||||
force_email_company=False,
|
||||
force_email_lang=False):
|
||||
""" Prepare rendering context for notification email.
|
||||
|
||||
Signature: if asked a default signature is computed based on author. Either
|
||||
@@ -3133,16 +3143,18 @@ class MailThread(models.AbstractModel):
|
||||
notification layout in the same language as the email content. A new
|
||||
parameter allows to force its value.
|
||||
|
||||
: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 model_description: model description used in email notification process
|
||||
(computed if not given);
|
||||
:param force_email_company: res.company record used when rendering notification
|
||||
layout. Otherwise computed based on current record;
|
||||
:param force_email_lang: lang used when rendering content, used notably to
|
||||
compute model name;
|
||||
:param record message: <mail.message> record being notified. May be
|
||||
void as 'msg_vals' superseeds it;
|
||||
:param dict msg_vals: values dict used to create the message, allows to
|
||||
skip message usage and spare some queries;
|
||||
:param str model_description: description of current model, given to
|
||||
avoid fetching it and easing translation support;
|
||||
:param record force_email_company: <res.company> record used when rendering
|
||||
notification layout. Otherwise computed based on current record;
|
||||
:param str force_email_lang: lang used when rendering content, used
|
||||
notably to compute model name or translate access buttons;
|
||||
|
||||
:return: dictionary of values used when rendering notification layout;
|
||||
"""
|
||||
if msg_vals is False:
|
||||
msg_vals = {}
|
||||
@@ -3226,12 +3238,15 @@ class MailThread(models.AbstractModel):
|
||||
}
|
||||
|
||||
def _notify_by_email_get_base_mail_values(self, message, additional_values=None):
|
||||
""" Add model-specific values to the dictionary used to create the
|
||||
notification email. Its base behavior is to compute model-specific
|
||||
headers.
|
||||
""" Return model-specific and message-related values to be used when
|
||||
creating notification emails. It serves as a common basis for all
|
||||
notification emails based on a given message.
|
||||
|
||||
:param dict base_mail_values: base mail.mail values, holding message
|
||||
to notify (mail_message_id and its fields), server, references, subject.
|
||||
:param record message: <mail.message> record being notified;
|
||||
:param dict additional_values: optional additional values to add (ease
|
||||
custom calls and inheritance);
|
||||
|
||||
:return: dictionary of values suitable for a <mail.mail> create;
|
||||
"""
|
||||
mail_subject = message.subject
|
||||
if not mail_subject and self:
|
||||
@@ -3265,14 +3280,20 @@ class MailThread(models.AbstractModel):
|
||||
base_mail_values['headers'] = repr(headers)
|
||||
return base_mail_values
|
||||
|
||||
def _notify_by_email_get_final_mail_values(self, recipient_ids, base_mail_values, additional_values=None):
|
||||
""" 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
|
||||
def _notify_by_email_get_final_mail_values(self, recipient_ids, mail_values,
|
||||
additional_values=None):
|
||||
""" Perform final formatting of values to create notification emails.
|
||||
Basic method just set the recipient partners as mail_mail recipients.
|
||||
Override to generate other mail values like email_to or email_cc.
|
||||
|
||||
:param list recipient_ids: res.partner IDs to notify;
|
||||
:param dict mail_values: notification mail values;
|
||||
:param dict additional_values: optional additional values to add (ease
|
||||
custom calls and inheritance);
|
||||
|
||||
:return: a new dictionary of values suitable for a <mail.mail> create;
|
||||
"""
|
||||
final_mail_values = dict(base_mail_values)
|
||||
final_mail_values = dict(mail_values)
|
||||
final_mail_values['recipient_ids'] = [Command.link(pid) for pid in recipient_ids]
|
||||
if additional_values:
|
||||
final_mail_values.update(additional_values)
|
||||
@@ -3282,8 +3303,10 @@ class MailThread(models.AbstractModel):
|
||||
""" Compute recipients to notify based on subtype and followers. This
|
||||
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
|
||||
:param record message: <mail.message> record being notified. May be
|
||||
void as 'msg_vals' superseeds it;
|
||||
:param dict msg_vals: values dict used to create the message, allows to
|
||||
skip message usage and spare some queries;
|
||||
|
||||
Kwargs allow to pass various parameters that are used by sub notification
|
||||
methods. See those methods for more details about supported parameters.
|
||||
@@ -3297,15 +3320,23 @@ class MailThread(models.AbstractModel):
|
||||
to avoid having several notifications / partner as it would make
|
||||
constraints crash. This is disabled by default to optimize speed;
|
||||
|
||||
:return list recipients_data: this is a list of recipients information (see
|
||||
``MailFollowers._get_recipient_data()`` for more details) formatted like
|
||||
[{'active': partner.active;
|
||||
'id': id of the res.partner;
|
||||
TDE/XDO TODO: flag rdata directly, for example r['notif'] = 'ocn_client'
|
||||
and r['needaction']=False and correctly override _notify_get_recipients
|
||||
|
||||
:return list recipients_data: list of recipients information (see
|
||||
``MailFollowers._get_recipient_data()`` for more details) formatted
|
||||
like [
|
||||
{
|
||||
'active': partner.active;
|
||||
'id': id of the res.partner being recipient to notify;
|
||||
'is_follower': follows the message related document;
|
||||
'lang': its lang;
|
||||
'groups': res.group IDs if linked to a user;
|
||||
'notif': 'inbox', 'email', 'sms' (SMS App);
|
||||
'share': partner.partner_share;
|
||||
'type': 'customer', 'portal', 'user;'
|
||||
}, {...}]
|
||||
'share': is partner a customer (partner.partner_share);
|
||||
'type': partner usage ('customer', 'portal', 'user');
|
||||
'ushare': are users shared (if users, all users are shared);
|
||||
}, {...}]
|
||||
"""
|
||||
msg_sudo = message.sudo()
|
||||
# get values from msg_vals or from message if msg_vals doen't exists
|
||||
@@ -3352,133 +3383,184 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
return recipients_data
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, 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.
|
||||
Groups is a list of tuple (group_name, group_func, group_data) where
|
||||
|
||||
* 'group_name' is an identifier used only to be able to override and
|
||||
manipulate groups;
|
||||
* 'group_func' is a function pointer taking a partner data dict as
|
||||
parameter. It is called on recipients to know if they belong to
|
||||
the group. Only first matching group is kept, iterating on the
|
||||
group list in order.
|
||||
* 'group_data' is a dict containing parameters used in notification
|
||||
process like {
|
||||
'active': if not, it is skipped in notification process (ease
|
||||
inheritance to be already present);
|
||||
'actions': list of actions to display as links or buttons in form
|
||||
{'url': link of the action, 'title': link or button
|
||||
string};
|
||||
'button_access': main access document button information, {'url'
|
||||
link of the access, 'title': link or button
|
||||
string};
|
||||
'has_button_access': display access document main button in email;
|
||||
'notification_group_name': name of the group, to ease usage;
|
||||
'recipients': list of partner IDs, will be fillup when evaluating
|
||||
groups;
|
||||
}
|
||||
|
||||
Default groups:
|
||||
|
||||
* 'user': recipients linked to an internal user;
|
||||
* 'portal': recipients linked to a portal user;
|
||||
* 'follower': recipients (not internal/portal users) follower of the
|
||||
related record;
|
||||
* 'customer': other recipients;
|
||||
|
||||
When having to find a group for recipients, the first matching one
|
||||
when iterating on groups is used. Reordering those groups is doable
|
||||
through override. Adding groups is a common override, to add specific
|
||||
buttons or actions for users belonging to some user groups.
|
||||
|
||||
:param record message: <mail.message> record being notified. May be
|
||||
void as 'msg_vals' superseeds it;
|
||||
:param str model_description: description of current model, given to
|
||||
avoid fetching it and easing translation support;
|
||||
:param dict msg_vals: values dict used to create the message, allows to
|
||||
skip message usage and spare some queries;
|
||||
|
||||
:return: list of groups definition
|
||||
"""
|
||||
is_thread_notification = self._notify_get_recipients_thread_info(msg_vals=msg_vals)['is_thread_notification']
|
||||
return [
|
||||
[
|
||||
'user',
|
||||
lambda pdata: pdata['type'] == 'user',
|
||||
{'has_button_access': is_thread_notification}
|
||||
{
|
||||
'active': True,
|
||||
'has_button_access': self._is_thread_message(msg_vals=msg_vals),
|
||||
}
|
||||
], [
|
||||
'portal',
|
||||
lambda pdata: pdata['type'] == 'portal',
|
||||
{'active': False, # activate only on demand if rights are enabled
|
||||
'has_button_access': False,
|
||||
{
|
||||
'active': False, # activate only on demand if rights are enabled
|
||||
'has_button_access': False,
|
||||
}
|
||||
], [
|
||||
'follower',
|
||||
lambda pdata: pdata['is_follower'],
|
||||
{'active': False, # activate only on demand if rights are enabled
|
||||
'has_button_access': False,
|
||||
{
|
||||
'active': False, # activate only on demand if rights are enabled
|
||||
'has_button_access': False,
|
||||
}
|
||||
], [
|
||||
'customer',
|
||||
lambda pdata: True,
|
||||
{'has_button_access': False}
|
||||
{
|
||||
'active': True,
|
||||
'has_button_access': False,
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
def _notify_get_recipients_classify(self, recipient_data, model_name, msg_vals=None):
|
||||
def _notify_get_recipients_groups_fillup(self, groups, model_description, msg_vals=None):
|
||||
""" Iterate on recipients groups (see '_notify_get_recipients_groups')
|
||||
and fill up the result with default values, allowing to compute links or
|
||||
titles once.
|
||||
|
||||
:param list groups: recipients groups;
|
||||
:param dict msg_vals: values dict used to create the message, allows to
|
||||
skip message usage and spare some queries;
|
||||
:param str model_description: description of current model, given to
|
||||
avoid fetching it and easing translation support;
|
||||
|
||||
:return: updated groups;
|
||||
"""
|
||||
access_link = self._notify_get_action_link('view', **msg_vals)
|
||||
|
||||
if model_description:
|
||||
view_title = _('View %s', model_description)
|
||||
else:
|
||||
view_title = _('View')
|
||||
|
||||
is_thread_message = self._is_thread_message(msg_vals=msg_vals)
|
||||
|
||||
# fill group_data with default_values if they are not complete
|
||||
for group_name, _group_func, group_data in groups:
|
||||
group_data.setdefault('active', True)
|
||||
group_data.setdefault('actions', [])
|
||||
group_data.setdefault('has_button_access', is_thread_message)
|
||||
group_data.setdefault('notification_group_name', group_name)
|
||||
group_data.setdefault('recipients', [])
|
||||
group_button_access = group_data.setdefault('button_access', {})
|
||||
group_button_access.setdefault('url', access_link)
|
||||
group_button_access.setdefault('title', view_title)
|
||||
|
||||
return groups
|
||||
|
||||
def _notify_get_recipients_classify(self, message, recipients_data,
|
||||
model_description, 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: list of recipients information (based on res.partner
|
||||
records). See ``MailThread._notify_get_recipients()``;
|
||||
:param record message: <mail.message> record being notified. May be
|
||||
void as 'msg_vals' superseeds it;
|
||||
:param list recipients_data: list of recipients data based on <res.partner>
|
||||
records formatted like [
|
||||
{
|
||||
'active': partner.active;
|
||||
'id': id of the res.partner being recipient to notify;
|
||||
'is_follower': follows the message related document;
|
||||
'lang': its lang;
|
||||
'groups': res.group IDs if linked to a user;
|
||||
'notif': 'inbox', 'email', 'sms' (SMS App);
|
||||
'share': is partner a customer (partner.partner_share);
|
||||
'type': partner usage ('customer', 'portal', 'user');
|
||||
'ushare': are users shared (if users, all users are shared);
|
||||
}, {...}]. See ``MailThread._notify_get_recipients()``;
|
||||
:param str model_description: description of current model, given to
|
||||
avoid fetching it and easing translation support;
|
||||
:param dict msg_vals: values dict used to create the message, allows to
|
||||
skip message usage and spare some queries;
|
||||
|
||||
:return list: list of groups formatted for notification processing like
|
||||
[{'active': True,
|
||||
'actions': [],
|
||||
'button_access': {},
|
||||
'has_button_access': False,
|
||||
'recipients': [11],},
|
||||
{'active': True,
|
||||
'actions': [],
|
||||
'button_access': {'title': 'View Simple Chatter Model',
|
||||
'url': '/mail/view?model=mail.test.simple&res_id=1497'},
|
||||
'has_button_access': True,
|
||||
'recipients': [4, 5, 6],},
|
||||
{'active': True,
|
||||
'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],}
|
||||
]
|
||||
:return list: list of groups (see '_notify_get_recipients_groups')
|
||||
with 'recipients' key filled with matching partners, like
|
||||
[{
|
||||
'active': True,
|
||||
'actions': [],
|
||||
'button_access': {},
|
||||
'has_button_access': False,
|
||||
'notification_group_name': 'user',
|
||||
'recipients': [11],
|
||||
}, {...}]
|
||||
"""
|
||||
# keep a local copy of msg_vals as it may be modified to include more information about groups or links
|
||||
# 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:
|
||||
is_thread_notification = self._notify_get_recipients_thread_info(msg_vals=local_msg_vals)['is_thread_notification']
|
||||
group_data.setdefault('active', True)
|
||||
group_data.setdefault('actions', list())
|
||||
group_data.setdefault('has_button_access', is_thread_notification)
|
||||
group_data.setdefault('notification_is_customer', False)
|
||||
group_data.setdefault('notification_group_name', group_name)
|
||||
group_data.setdefault('recipients', list())
|
||||
group_button_access = group_data.setdefault('button_access', {})
|
||||
group_button_access.setdefault('url', access_link)
|
||||
group_button_access.setdefault('title', view_title)
|
||||
groups = self._notify_get_recipients_groups_fillup(
|
||||
self._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=local_msg_vals
|
||||
),
|
||||
model_description,
|
||||
msg_vals=local_msg_vals
|
||||
)
|
||||
|
||||
# classify recipients in each group
|
||||
for recipient in recipient_data:
|
||||
for group_name, group_func, group_data in groups:
|
||||
if group_data['active'] and group_func(recipient):
|
||||
group_data['recipients'].append(recipient['id'])
|
||||
for recipient_data in recipients_data:
|
||||
for _group_name, group_func, group_data in groups:
|
||||
if group_data['active'] and group_func(recipient_data):
|
||||
group_data['recipients'].append(recipient_data['id'])
|
||||
break
|
||||
|
||||
# filter out groups without recipients
|
||||
return [group_data for _group_name, _group_func, group_data in groups
|
||||
if group_data['recipients']]
|
||||
|
||||
def _notify_get_recipients_thread_info(self, msg_vals=None):
|
||||
""" Tool method to compute thread info used in ``_notify_classify_recipients``
|
||||
and its sub-methods. """
|
||||
res_model = msg_vals['model'] if (msg_vals and 'model' in msg_vals) else self._name
|
||||
res_id = msg_vals['res_id'] if (msg_vals and 'res_id' in msg_vals) else (self.ids[0] if self.ids else False)
|
||||
return {
|
||||
'is_thread_notification': bool(res_id) if (res_model and res_model != 'mail.thread') else False
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _notify_encode_link(self, base_link, params):
|
||||
secret = self.env['ir.config_parameter'].sudo().get_param('database.secret')
|
||||
token = '%s?%s' % (base_link, ' '.join('%s=%s' % (key, params[key]) for key in sorted(params)))
|
||||
hm = hmac.new(secret.encode('utf-8'), token.encode('utf-8'), hashlib.sha1).hexdigest()
|
||||
return hm
|
||||
return [
|
||||
group_data
|
||||
for _group_name, _group_func, group_data in groups
|
||||
if group_data['recipients']
|
||||
]
|
||||
|
||||
def _notify_get_action_link(self, link_type, **kwargs):
|
||||
""" Prepare link to an action: view document, follow document, ... """
|
||||
@@ -3507,7 +3589,7 @@ class MailThread(models.AbstractModel):
|
||||
return ''
|
||||
|
||||
if link_type not in ['view']:
|
||||
token = self._notify_encode_link(base_link, params)
|
||||
token = self._encode_link(base_link, params)
|
||||
params['token'] = token
|
||||
|
||||
link = '%s?%s' % (base_link, urls.url_encode(params, sort=True))
|
||||
@@ -3516,6 +3598,24 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
return link
|
||||
|
||||
@api.model
|
||||
def _encode_link(self, base_link, params):
|
||||
secret = self.env['ir.config_parameter'].sudo().get_param('database.secret')
|
||||
token = '%s?%s' % (base_link, ' '.join('%s=%s' % (key, params[key]) for key in sorted(params)))
|
||||
hm = hmac.new(secret.encode('utf-8'), token.encode('utf-8'), hashlib.sha1).hexdigest()
|
||||
return hm
|
||||
|
||||
def _is_thread_message(self, msg_vals=None):
|
||||
""" Tool method to compute thread validity in notification methods.
|
||||
msg_vals is used as a replacement for self, allowing to force model
|
||||
and res_id independently of current recordset. Void values in dict
|
||||
are kept e.g. model=False is valid. """
|
||||
if msg_vals is None:
|
||||
msg_vals = {}
|
||||
res_model = msg_vals['model'] if 'model' in msg_vals else self._name
|
||||
res_id = msg_vals['res_id'] if 'res_id' in msg_vals else (self.ids[0] if self.ids else False)
|
||||
return bool(res_id) if (res_model and res_model != 'mail.thread') else False
|
||||
|
||||
# ------------------------------------------------------
|
||||
# FOLLOWERS API
|
||||
# ------------------------------------------------------
|
||||
|
||||
@@ -16,8 +16,10 @@ class MailThread(models.AbstractModel):
|
||||
domain=lambda self: [('model', '=', self._name), '|', ('message_type', '=', 'comment'), ('message_type', '=', 'email')], auto_join=True,
|
||||
help="Website communication history")
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
groups = super()._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
@@ -37,11 +39,11 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
new_group = [
|
||||
('portal_customer', lambda pdata: pdata['id'] == customer.id, {
|
||||
'has_button_access': True,
|
||||
'active': True,
|
||||
'button_access': {
|
||||
'url': access_link,
|
||||
},
|
||||
'notification_is_customer': True,
|
||||
'has_button_access': True,
|
||||
})
|
||||
]
|
||||
else:
|
||||
|
||||
@@ -2440,12 +2440,14 @@ class Task(models.Model):
|
||||
res -= waiting_subtype
|
||||
return res
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, 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_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
@@ -2464,7 +2466,10 @@ class Task(models.Model):
|
||||
groups.insert(0, (
|
||||
'allowed_portal_users',
|
||||
lambda pdata: pdata['type'] == 'portal',
|
||||
{}
|
||||
{
|
||||
'active': True,
|
||||
'has_button_access': True,
|
||||
}
|
||||
))
|
||||
portal_privacy = self.project_id.privacy_visibility == 'portal'
|
||||
for group_name, _group_method, group_data in groups:
|
||||
|
||||
@@ -341,10 +341,12 @@ class PurchaseOrder(models.Model):
|
||||
kwargs['notify_author'] = self.env.user.partner_id.id in (kwargs.get('partner_ids') or [])
|
||||
return super(PurchaseOrder, self.with_context(**po_ctx)).message_post(**kwargs)
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" Tweak 'view document' button for portal customers, calling directly
|
||||
routes for confirm specific to PO model. """
|
||||
groups = super(PurchaseOrder, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
|
||||
@@ -1249,11 +1249,13 @@ class SaleOrder(models.Model):
|
||||
kwargs['notify_author'] = self.env.user.partner_id.id in (kwargs.get('partner_ids') or [])
|
||||
return super(SaleOrder, self.with_context(**so_ctx)).message_post(**kwargs)
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" Give access button to users and portal customer as portal is integrated
|
||||
in sale. Customer and portal group have probably no right to see
|
||||
the document so they don't have the access button. """
|
||||
groups = super()._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@ class Followers(models.Model):
|
||||
_inherit = ['mail.followers']
|
||||
|
||||
def _get_recipient_data(self, records, message_type, subtype_id, pids=None):
|
||||
recipients_data = super()._get_recipient_data(records, message_type, subtype_id, pids=pids)
|
||||
if message_type != 'sms' or not (pids or records):
|
||||
return super(Followers, self)._get_recipient_data(records, message_type, subtype_id, pids=pids)
|
||||
return recipients_data
|
||||
|
||||
if pids is None and records:
|
||||
records_pids = dict(
|
||||
@@ -20,7 +21,6 @@ class Followers(models.Model):
|
||||
records_pids = dict((record.id, pids) for record in records)
|
||||
else:
|
||||
records_pids = {0: pids if pids else []}
|
||||
recipients_data = super(Followers, self)._get_recipient_data(records, message_type, subtype_id, pids=pids)
|
||||
for rid, rdata in recipients_data.items():
|
||||
sms_pids = records_pids.get(rid) or []
|
||||
for pid, pdata in rdata.items():
|
||||
|
||||
@@ -72,8 +72,10 @@ class MailTestLang(models.Model):
|
||||
def _mail_get_partner_fields(self):
|
||||
return ['customer_id']
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
groups = super(MailTestLang, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
|
||||
local_msg_vals = dict(msg_vals or {})
|
||||
|
||||
|
||||
@@ -160,11 +160,13 @@ class MailTestTicket(models.Model):
|
||||
for record in self
|
||||
)
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" Activate more groups to test query counters notably (and be backward
|
||||
compatible for tests). """
|
||||
local_msg_vals = dict(msg_vals or {})
|
||||
groups = super()._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
for group_name, _group_method, group_data in groups:
|
||||
if group_name == 'portal':
|
||||
group_data['active'] = True
|
||||
@@ -311,10 +313,12 @@ class MailTestContainer(models.Model):
|
||||
for record in self
|
||||
)
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" Activate more groups to test query counters notably (and be backward
|
||||
compatible for tests). """
|
||||
groups = super(MailTestContainer, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
for group_name, _group_method, group_data in groups:
|
||||
if group_name == 'portal':
|
||||
group_data['active'] = True
|
||||
|
||||
@@ -222,7 +222,8 @@ class TestMailNotifyAPI(TestMessagePostCommon):
|
||||
|
||||
# test notifying the class (void recordset)
|
||||
classify_res = self.env[base_record._name]._notify_get_recipients_classify(
|
||||
pdata, 'My Custom Model Name', msg_vals=notify_msg_vals
|
||||
self.env['mail.message'], 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)
|
||||
@@ -249,7 +250,9 @@ class TestMailNotifyAPI(TestMessagePostCommon):
|
||||
'res_id': res_id,
|
||||
})
|
||||
classify_res = self.env[model].browse(res_id)._notify_get_recipients_classify(
|
||||
pdata, 'Test', msg_vals=notify_msg_vals)
|
||||
self.env['mail.message'], pdata, 'Test',
|
||||
msg_vals=notify_msg_vals,
|
||||
)
|
||||
# find back information for partner
|
||||
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)
|
||||
@@ -271,7 +274,9 @@ class TestMailNotifyAPI(TestMessagePostCommon):
|
||||
'res_id': res_id,
|
||||
})
|
||||
classify_res = base_record._notify_get_recipients_classify(
|
||||
pdata, 'Test', msg_vals=notify_msg_vals)
|
||||
self.env['mail.message'], pdata, 'Test',
|
||||
msg_vals=notify_msg_vals,
|
||||
)
|
||||
# find back information for partner
|
||||
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)
|
||||
|
||||
@@ -56,7 +56,9 @@ class TestMailThreadInternals(TestMailThreadInternalsCommon):
|
||||
'By default access tokens are False with portal'
|
||||
)
|
||||
|
||||
groups = test_record._notify_get_recipients_groups()
|
||||
groups = test_record._notify_get_recipients_groups(
|
||||
self.env['mail.message'], False,
|
||||
)
|
||||
portal_customer_group = next(
|
||||
(group for group in groups if group[0] == 'portal_customer'),
|
||||
False
|
||||
|
||||
@@ -182,7 +182,9 @@ class TestPortalFlow(MailCommon, HttpCase):
|
||||
'subject': 'Your quotation "{{ object.name }}"',
|
||||
})
|
||||
cls._create_portal_user()
|
||||
for group_name, group_func, group_data in cls.record_portal.sudo()._notify_get_recipients_groups():
|
||||
for group_name, group_func, group_data in cls.record_portal.sudo()._notify_get_recipients_groups(
|
||||
cls.env['mail.message'], False
|
||||
):
|
||||
if group_name == 'portal_customer' and group_func(cls.customer):
|
||||
cls.record_access_url = group_data['button_access']['url']
|
||||
break
|
||||
|
||||
@@ -274,9 +274,11 @@ class BlogPost(models.Model):
|
||||
'res_id': self.id,
|
||||
}
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" Add access button to everyone if the document is published. """
|
||||
groups = super(BlogPost, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
|
||||
@@ -934,9 +934,11 @@ class Post(models.Model):
|
||||
'res_id': self.id,
|
||||
}
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" Add access button to everyone if the document is active. """
|
||||
groups = super(Post, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
|
||||
@@ -402,10 +402,12 @@ class SaleOrder(models.Model):
|
||||
self.cart_recovery_email_sent = True
|
||||
return super(SaleOrder, self)._message_post_after_hook(message, msg_vals)
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" In case of cart recovery email, update link to redirect directly
|
||||
to the cart (like ``mail_template_sale_cart_recovery`` template). """
|
||||
groups = super(SaleOrder, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
|
||||
@@ -735,9 +735,11 @@ class Slide(models.Model):
|
||||
}
|
||||
return super(Slide, self)._get_access_action(access_uid=access_uid, force_website=force_website)
|
||||
|
||||
def _notify_get_recipients_groups(self, msg_vals=None):
|
||||
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||||
""" Add access button to everyone if the document is active. """
|
||||
groups = super(Slide, self)._notify_get_recipients_groups(msg_vals=msg_vals)
|
||||
groups = super()._notify_get_recipients_groups(
|
||||
message, model_description, msg_vals=msg_vals
|
||||
)
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
|
||||
Reference in New Issue
Block a user