summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/mail/models/mail_activity.py5
-rw-r--r--addons/mail/models/mail_mail.py4
-rw-r--r--addons/mail/models/mail_message.py122
-rw-r--r--addons/mail/models/mail_thread.py35
-rw-r--r--addons/mail/static/src/js/models/messages/message.js2
-rw-r--r--addons/test_mail/tests/test_message_compose.py4
-rw-r--r--addons/test_mail/tests/test_performance.py2
7 files changed, 111 insertions, 63 deletions
diff --git a/addons/mail/models/mail_activity.py b/addons/mail/models/mail_activity.py
index a989ac4c983..ab098ed52a1 100644
--- a/addons/mail/models/mail_activity.py
+++ b/addons/mail/models/mail_activity.py
@@ -386,13 +386,14 @@ class MailActivity(models.Model):
engine='ir.qweb',
minimal_qcontext=True
)
- self.env['mail.thread'].message_notify(
+ record = self.env[activity.res_model].browse(activity.res_id)
+ record.message_notify(
partner_ids=activity.user_id.partner_id.ids,
body=body,
subject=_('%s: %s assigned to you') % (activity.res_name, activity.summary or activity.activity_type_id.name),
record_name=activity.res_name,
model_description=model_description,
- notif_layout='mail.mail_notification_light'
+ notif_layout='mail.mail_notification_light',
)
@api.multi
diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py
index a2ba820656f..ffc8aa33399 100644
--- a/addons/mail/models/mail_mail.py
+++ b/addons/mail/models/mail_mail.py
@@ -168,7 +168,7 @@ class MailMail(models.Model):
'failure_type': failure_type,
'failure_reason': failure_reason,
})
- messages = notifications.mapped('mail_message_id').filtered(lambda m: m.res_id and m.model)
+ messages = notifications.mapped('mail_message_id').filtered(lambda m: m.is_thread_message())
messages._notify_failure_update() # notify user that we have a failure
(notifications - failed).sudo().write({
'email_status': 'sent',
@@ -312,7 +312,7 @@ class MailMail(models.Model):
bounce_alias = ICP.get_param("mail.bounce.alias")
catchall_domain = ICP.get_param("mail.catchall.domain")
if bounce_alias and catchall_domain:
- if mail.model and mail.res_id:
+ if mail.mail_message_id.is_thread_message():
headers['Return-Path'] = '%s+%d-%s-%d@%s' % (bounce_alias, mail.id, mail.model, mail.res_id, catchall_domain)
else:
headers['Return-Path'] = '%s+%d@%s' % (bounce_alias, mail.id, catchall_domain)
diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py
index c9a54701ca7..0e595654885 100644
--- a/addons/mail/models/mail_message.py
+++ b/addons/mail/models/mail_message.py
@@ -59,7 +59,8 @@ class Message(models.Model):
message_type = fields.Selection([
('email', 'Email'),
('comment', 'Comment'),
- ('notification', 'System notification')],
+ ('notification', 'System notification'),
+ ('user_notification', 'User Specific Notification')],
'Type', required=True, default='email',
help="Message type: email for email message, notification for system "
"message, comment for other messages such as user replies",
@@ -79,8 +80,7 @@ class Message(models.Model):
author_avatar = fields.Binary("Author's avatar", related='author_id.image_small', readonly=False)
# recipients: include inactive partners (they may have been archived after
# the message was sent, but they should remain visible in the relation)
- partner_ids = fields.Many2many('res.partner', string='Recipients',
- context={'active_test': False})
+ partner_ids = fields.Many2many('res.partner', string='Recipients', context={'active_test': False})
needaction_partner_ids = fields.Many2many(
'res.partner', 'mail_message_res_partner_needaction_rel', string='Partners with Need Action',
context={'active_test': False})
@@ -393,7 +393,8 @@ class Message(models.Model):
customer_email_data.append((partner_tree[notification.res_partner_id.id][0], partner_tree[notification.res_partner_id.id][1], notification.email_status))
has_access_to_model = message.model and self.env[message.model].check_access_rights('read', raise_exception=False)
- main_attachment = has_access_to_model and message.res_id and self.env[message.model].search([('id', '=',message.res_id)]) and getattr(self.env[message.model].browse(message.res_id), 'message_main_attachment_id')
+ if message.attachment_ids:
+ main_attachment = has_access_to_model and message.res_id and self.env[message.model].search([('id', '=', message.res_id)]) and getattr(self.env[message.model].browse(message.res_id), 'message_main_attachment_id')
attachment_ids = []
for attachment in message.attachment_ids:
if attachment.id in attachments_tree:
@@ -417,7 +418,13 @@ class Message(models.Model):
@api.multi
def message_fetch_failed(self):
- messages = self.search([('has_error', '=', True), ('author_id.id', '=', self.env.user.partner_id.id), ('res_id', '!=', 0), ('model', '!=', False)])
+ messages = self.search([
+ ('has_error', '=', True),
+ ('author_id.id', '=', self.env.user.partner_id.id),
+ ('res_id', '!=', 0),
+ ('model', '!=', False),
+ ('message_type', '!=', 'user_notification')
+ ])
return messages._format_mail_failures()
@api.model
@@ -515,7 +522,7 @@ class Message(models.Model):
message['needaction_partner_ids'] = notif_dict.get(message['id'], dict()).get('partner_id', [])
message['is_note'] = message['subtype_id'] and subtypes_dict[message['subtype_id'][0]]['id'] == note_id
message['is_discussion'] = message['subtype_id'] and subtypes_dict[message['subtype_id'][0]]['id'] == com_id
- message['is_notification'] = message['is_note'] and not message['model'] and not message['res_id']
+ message['is_notification'] = message['message_type'] == 'user_notification'
message['subtype_description'] = message['subtype_id'] and subtypes_dict[message['subtype_id'][0]]['description']
if message['model'] and self.env[message['model']]._original_module:
message['module_icon'] = modules.module.get_module_icon(self.env[message['model']]._original_module)
@@ -583,6 +590,19 @@ class Message(models.Model):
if not self._cr.fetchone():
self._cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""")
+ @api.multi
+ def is_thread_message(self, vals=None):
+ if vals:
+ res_id = vals.get('res_id')
+ model = vals.get('model')
+ message_type = vals.get('message_type')
+ else:
+ self.ensure_one()
+ res_id = self.res_id
+ model = self.model
+ message_type = self.message_type
+ return res_id and model and message_type != 'user_notification'
+
@api.model
def _find_allowed_model_wise(self, doc_model, doc_dict):
doc_ids = list(doc_dict)
@@ -640,7 +660,7 @@ class Message(models.Model):
super(Message, self.sudo(access_rights_uid or self._uid)).check_access_rights('read')
self._cr.execute("""
- SELECT DISTINCT m.id, m.model, m.res_id, m.author_id,
+ SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.message_type,
COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id),
channel_partner.channel_id as channel_id
FROM "%s" m
@@ -656,14 +676,14 @@ class Message(models.Model):
ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = %%(pid)s
WHERE m.id = ANY (%%(ids)s)""" % self._table, dict(pid=pid, ids=ids))
- for id, rmod, rid, author_id, partner_id, channel_id in self._cr.fetchall():
+ for id, rmod, rid, author_id, message_type, partner_id, channel_id in self._cr.fetchall():
if author_id == pid:
author_ids.add(id)
elif partner_id == pid:
partner_ids.add(id)
elif channel_id:
channel_ids.add(id)
- elif rmod and rid:
+ elif rmod and rid and message_type != 'user_notification':
model_ids.setdefault(rmod, {}).setdefault(rid, set()).add(id)
allowed_ids = self._find_allowed_doc_ids(model_ids)
@@ -733,13 +753,14 @@ class Message(models.Model):
(self._description, operation))
# Read mail_message.ids to have their values
- message_values = dict((res_id, {}) for res_id in self.ids)
+ message_values = dict((message_id, {}) for message_id in self.ids)
if operation == 'read':
self._cr.execute("""
SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.parent_id,
COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id),
- channel_partner.channel_id as channel_id, m.moderation_status
+ channel_partner.channel_id as channel_id, m.moderation_status,
+ m.message_type as message_type
FROM "%s" m
LEFT JOIN "mail_message_res_partner_rel" partner_rel
ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = %%(pid)s
@@ -752,7 +773,7 @@ class Message(models.Model):
LEFT JOIN "mail_channel_partner" channel_partner
ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = %%(pid)s
WHERE m.id = ANY (%%(ids)s)""" % self._table, dict(pid=self.env.user.partner_id.id, ids=self.ids))
- for mid, rmod, rid, author_id, parent_id, partner_id, channel_id, moderation_status in self._cr.fetchall():
+ for mid, rmod, rid, author_id, parent_id, partner_id, channel_id, moderation_status, message_type in self._cr.fetchall():
message_values[mid] = {
'model': rmod,
'res_id': rid,
@@ -760,13 +781,15 @@ class Message(models.Model):
'parent_id': parent_id,
'moderation_status': moderation_status,
'moderator_id': False,
- 'notified': any((message_values[mid].get('notified'), partner_id, channel_id))
+ 'notified': any((message_values[mid].get('notified'), partner_id, channel_id)),
+ 'message_type': message_type,
}
elif operation == 'write':
self._cr.execute("""
SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.parent_id, m.moderation_status,
COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id),
- channel_partner.channel_id as channel_id, channel_moderator_rel.res_users_id as moderator_id
+ channel_partner.channel_id as channel_id, channel_moderator_rel.res_users_id as moderator_id,
+ m.message_type as message_type
FROM "%s" m
LEFT JOIN "mail_message_res_partner_rel" partner_rel
ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = %%(pid)s
@@ -783,7 +806,7 @@ class Message(models.Model):
LEFT JOIN "mail_channel_moderator_rel" channel_moderator_rel
ON channel_moderator_rel.mail_channel_id = moderated_channel.id AND channel_moderator_rel.res_users_id = %%(uid)s
WHERE m.id = ANY (%%(ids)s)""" % self._table, dict(pid=self.env.user.partner_id.id, uid=self.env.user.id, ids=self.ids))
- for mid, rmod, rid, author_id, parent_id, moderation_status, partner_id, channel_id, moderator_id in self._cr.fetchall():
+ for mid, rmod, rid, author_id, parent_id, moderation_status, partner_id, channel_id, moderator_id, message_type in self._cr.fetchall():
message_values[mid] = {
'model': rmod,
'res_id': rid,
@@ -791,35 +814,38 @@ class Message(models.Model):
'parent_id': parent_id,
'moderation_status': moderation_status,
'moderator_id': moderator_id,
- 'notified': any((message_values[mid].get('notified'), partner_id, channel_id))
+ 'notified': any((message_values[mid].get('notified'), partner_id, channel_id)),
+ 'message_type': message_type,
}
elif operation == 'create':
- self._cr.execute("""SELECT DISTINCT id, model, res_id, author_id, parent_id, moderation_status FROM "%s" WHERE id = ANY (%%s)""" % self._table, (self.ids,))
- for mid, rmod, rid, author_id, parent_id, moderation_status in self._cr.fetchall():
+ self._cr.execute("""SELECT DISTINCT id, model, res_id, author_id, parent_id, moderation_status, message_type FROM "%s" WHERE id = ANY (%%s)""" % self._table, (self.ids,))
+ for mid, rmod, rid, author_id, parent_id, moderation_status, message_type in self._cr.fetchall():
message_values[mid] = {
'model': rmod,
'res_id': rid,
'author_id': author_id,
'parent_id': parent_id,
'moderation_status': moderation_status,
- 'moderator_id': False
+ 'moderator_id': False,
+ 'message_type': message_type,
}
else: # unlink
- self._cr.execute("""SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.parent_id, m.moderation_status, channel_moderator_rel.res_users_id as moderator_id
+ self._cr.execute("""SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.parent_id, m.moderation_status, channel_moderator_rel.res_users_id as moderator_id, m.message_type as message_type
FROM "%s" m
LEFT JOIN "mail_channel" moderated_channel
ON m.moderation_status = 'pending_moderation' AND m.res_id = moderated_channel.id
LEFT JOIN "mail_channel_moderator_rel" channel_moderator_rel
ON channel_moderator_rel.mail_channel_id = moderated_channel.id AND channel_moderator_rel.res_users_id = (%%s)
WHERE m.id = ANY (%%s)""" % self._table, (self.env.user.id, self.ids,))
- for mid, rmod, rid, author_id, parent_id, moderation_status, moderator_id in self._cr.fetchall():
+ for mid, rmod, rid, author_id, parent_id, moderation_status, moderator_id, message_type in self._cr.fetchall():
message_values[mid] = {
'model': rmod,
'res_id': rid,
'author_id': author_id,
'parent_id': parent_id,
'moderation_status': moderation_status,
- 'moderator_id': moderator_id
+ 'moderator_id': moderator_id,
+ 'message_type': message_type,
}
# Author condition (READ, WRITE, CREATE (private))
@@ -832,7 +858,7 @@ class Message(models.Model):
if message.get('moderation_status') != 'pending_moderation' and message.get('author_id') == self.env.user.partner_id.id]
elif operation == 'create':
author_ids = [mid for mid, message in message_values.items()
- if not message.get('model') and not message.get('res_id')]
+ if not self.is_thread_message(message)]
# Parent condition, for create (check for received notifications for the created message parent)
notified_ids = []
@@ -873,7 +899,10 @@ class Message(models.Model):
])
fol_mids = [follower.res_id for follower in followers]
notified_ids += [mid for mid, message in message_values.items()
- if message.get('model') == doc_model and message.get('res_id') in fol_mids]
+ if message.get('model') == doc_model and
+ message.get('res_id') in fol_mids and
+ message.get('message_type') != 'user_notification'
+ ]
# CRUD: Access rights related to the document
other_ids = other_ids.difference(set(notified_ids))
@@ -886,13 +915,13 @@ class Message(models.Model):
DocumentModel.check_mail_message_access(mids.ids, operation) # ?? mids ?
else:
self.env['mail.thread'].check_mail_message_access(mids.ids, operation, model_name=model)
- if operation in ['write', 'unlink']:
- document_related_ids += [mid for mid, message in message_values.items()
- if message.get('model') == model and message.get('res_id') in mids.ids and
- message.get('moderation_status') != 'pending_moderation']
- else:
- document_related_ids += [mid for mid, message in message_values.items()
- if message.get('model') == model and message.get('res_id') in mids.ids]
+ document_related_ids += [
+ mid for mid, message in message_values.items()
+ if (message.get('model') == model and
+ message.get('res_id') in mids.ids and
+ message.get('message_type') != 'user_notification' and
+ (message.get('moderation_status') != 'pending_moderation' or
+ operation not in ['write', 'unlink']))]
# Calculate remaining ids: if not void, raise an error
other_ids = other_ids.difference(set(document_related_ids))
@@ -915,15 +944,22 @@ class Message(models.Model):
@api.model
def _get_reply_to(self, values):
""" Return a specific reply_to for the document """
- model, res_id, email_from = values.get('model', self._context.get('default_model')), values.get('res_id', self._context.get('default_res_id')), values.get('email_from') # ctx values / defualt_get res ?
- records = self.env[model].browse([res_id]) if model and res_id else None
- return self.env['mail.thread']._notify_get_reply_to_on_records(default=email_from, records=records)[res_id or False]
+ model = values.get('model', self._context.get('default_model'))
+ res_id = values.get('res_id', self._context.get('default_res_id'))
+ email_from = values.get('email_from')
+ message_type = values.get('message_type')
+ records = None
+ if self.is_thread_message({'model': model, 'res_id': res_id, 'message_type': message_type}):
+ records = self.env[model].browse([res_id])
+ else:
+ res_id = False
+ return self.env['mail.thread']._notify_get_reply_to_on_records(default=email_from, records=records)[res_id]
@api.model
def _get_message_id(self, values):
if values.get('no_auto_thread', False) is True:
message_id = tools.generate_tracking_message_id('reply_to')
- elif values.get('res_id') and values.get('model'):
+ elif self.is_thread_message(values):
message_id = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values)
else:
message_id = tools.generate_tracking_message_id('private')
@@ -933,7 +969,7 @@ class Message(models.Model):
def _invalidate_documents(self):
""" Invalidate the cache of the documents followed by ``self``. """
for record in self:
- if record.model and record.res_id and 'message_ids' in self.env[record.model]:
+ if record.is_thread_message() and 'message_ids' in self.env[record.model]:
self.env[record.model].invalidate_cache(fnames=[
'message_ids',
'message_unread',
@@ -996,7 +1032,7 @@ class Message(models.Model):
if other_cmd:
message.sudo().write({'tracking_value_ids': tracking_values_cmd})
- if values.get('model') and values.get('res_id'):
+ if message.is_thread_message(values):
message._invalidate_documents()
return message
@@ -1010,13 +1046,14 @@ class Message(models.Model):
@api.multi
def write(self, vals):
- if 'model' in vals or 'res_id' in vals:
+ record_changed = 'model' in vals or 'res_id' in vals
+ if record_changed or 'message_type' in vals:
self._invalidate_documents()
res = super(Message, self).write(vals)
if vals.get('attachment_ids'):
for mail in self:
mail.attachment_ids.check(mode='read')
- if 'notification_ids' in vals or 'model' in vals or 'res_id' in vals:
+ if 'notification_ids' in vals or record_changed:
self._invalidate_documents()
return res
@@ -1058,6 +1095,7 @@ class Message(models.Model):
notification templates);
:param mail_auto_delete: delete notification emails once sent;
"""
+
msg_vals = msg_vals if msg_vals else {}
rdata = self._notify_compute_recipients(record, msg_vals)
return self._notify_recipients(
@@ -1079,7 +1117,9 @@ class Message(models.Model):
'partners': [],
'channels': [],
}
- res = self.env['mail.followers']._get_recipient_data(record, subtype_id, pids, cids)
+ res = []
+ user_notification = msg_vals.get('message_type') == 'user_notification'
+ res = self.env['mail.followers']._get_recipient_data(not user_notification and record, subtype_id, pids, cids)
author_id = msg_vals.get('author_id') or self.author_id.id if res else False
for pid, cid, active, pshare, ctype, notif, groups in res:
if pid and pid == author_id and not self.env.context.get('mail_notify_author'): # do not notify the author of its own messages
@@ -1214,7 +1254,7 @@ class Message(models.Model):
})
# proceed with notification process to send notification emails and Inbox messages
for message in self:
- record = self.env[message.model].browse(message.res_id) if message.model and message.res_id else None
+ record = self.env[message.model].browse(message.res_id) if message.is_thread_message() else None
message._notify(record, {})
@api.multi
diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index b55f5cc0937..b53f9713583 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -91,7 +91,7 @@ class MailThread(models.AbstractModel):
compute='_get_followers', search='_search_follower_channels')
message_ids = fields.One2many(
'mail.message', 'res_id', string='Messages',
- domain=lambda self: [('model', '=', self._name)], auto_join=True)
+ domain=lambda self: [('model', '=', self._name), ('message_type', '!=', 'user_notification')], auto_join=True)
message_unread = fields.Boolean(
'Unread Messages', compute='_get_message_unread',
help="If checked new messages require your attention.")
@@ -187,8 +187,9 @@ class MailThread(models.AbstractModel):
ON (cp.channel_id = rel.mail_channel_id AND cp.partner_id = %s AND
(cp.seen_message_id IS NULL OR cp.seen_message_id < msg.id))
WHERE msg.model = %s AND msg.res_id = ANY(%s) AND
+ msg.message_type != 'user_notification' AND
(msg.author_id IS NULL OR msg.author_id != %s) AND
- (msg.message_type != 'notification' OR msg.model != 'mail.channel')""",
+ (msg.message_type not in ('notification', 'user_notification') OR msg.model != 'mail.channel')""",
(partner_id, self._name, list(self.ids), partner_id,))
for result in self._cr.fetchall():
res[result[0]] += 1
@@ -205,7 +206,7 @@ class MailThread(models.AbstractModel):
self._cr.execute(""" SELECT msg.res_id FROM mail_message msg
RIGHT JOIN mail_message_res_partner_needaction_rel rel
ON rel.mail_message_id = msg.id AND rel.res_partner_id = %s AND (rel.is_read = false OR rel.is_read IS NULL)
- WHERE msg.model = %s AND msg.res_id in %s""",
+ WHERE msg.model = %s AND msg.res_id in %s AND msg.message_type != 'user_notification'""",
(self.env.user.partner_id.id, self._name, tuple(self.ids),))
for result in self._cr.fetchall():
res[result[0]] += 1
@@ -225,7 +226,7 @@ class MailThread(models.AbstractModel):
self._cr.execute(""" SELECT msg.res_id, COUNT(msg.res_id) FROM mail_message msg
RIGHT JOIN mail_message_res_partner_needaction_rel rel
ON rel.mail_message_id = msg.id AND rel.email_status in ('exception','bounce')
- WHERE msg.author_id = %s AND msg.model = %s AND msg.res_id in %s
+ WHERE msg.author_id = %s AND msg.model = %s AND msg.res_id in %s AND msg.message_type != 'user_notification'
GROUP BY msg.res_id""",
(self.env.user.partner_id.id, self._name, tuple(self.ids),))
res.update(self._cr.fetchall())
@@ -341,7 +342,7 @@ class MailThread(models.AbstractModel):
cascaded, because link is done through (res_model, res_id). """
if not self:
return True
- self.env['mail.message'].search([('model', '=', self._name), ('res_id', 'in', self.ids)]).unlink()
+ self.env['mail.message'].search([('model', '=', self._name), ('res_id', 'in', self.ids), ('message_type', '!=', 'user_notification')]).unlink()
res = super(MailThread, self).unlink()
self.env['mail.followers'].sudo().search(
[('res_model', '=', self._name), ('res_id', 'in', self.ids)]
@@ -534,10 +535,12 @@ class MailThread(models.AbstractModel):
msg_comment = MailMessage.search([
('model', '=', self._name),
('res_id', '=', self.id),
+ ('message_type', '!=', 'user_notification'),
('subtype_id', '=', subtype_comment)])
msg_not_comment = MailMessage.search([
('model', '=', self._name),
('res_id', '=', self.id),
+ ('message_type', '!=', 'user_notification'),
('subtype_id', '!=', subtype_comment)])
# update the messages
@@ -1948,7 +1951,7 @@ class MailThread(models.AbstractModel):
# set in the context.
model = False
if self.ids:
- self.ensure_one()
+ self.ensure_one() # not so usefull
model = kwargs.get('model', False) if self._name == 'mail.thread' else self._name
if model and model != self._name and hasattr(self.env[model], 'message_post'):
return self.env[model].browse(self.ids).message_post(
@@ -1991,8 +1994,10 @@ class MailThread(models.AbstractModel):
subtype = 'mail.%s' % subtype
subtype_id = self.env['ir.model.data'].xmlid_to_res_id(subtype)
+ user_notification = message_type == 'user_notification'
+
# automatically subscribe recipients if asked to
- if self._context.get('mail_post_autofollow') and self.ids and partner_ids:
+ if self._context.get('mail_post_autofollow') and self.ids and not user_notification and partner_ids:
partner_to_subscribe = partner_ids
if self._context.get('mail_post_autofollow_partner_ids'):
partner_to_subscribe = [p for p in partner_ids if p in self._context.get('mail_post_autofollow_partner_ids')]
@@ -2000,8 +2005,8 @@ class MailThread(models.AbstractModel):
# _mail_flat_thread: automatically set free messages to the first posted message
MailMessage = self.env['mail.message']
- if self._mail_flat_thread and model and not parent_id and self.ids:
- messages = MailMessage.search(['&', ('res_id', '=', self.ids[0]), ('model', '=', model)], order="id ASC", limit=1)
+ if self._mail_flat_thread and model and not parent_id and self.ids and not user_notification:
+ messages = MailMessage.search(['&', ('res_id', '=', self.ids[0]), ('model', '=', model), ('message_type', '!=', 'user_notification')], order="id ASC", limit=1)
parent_id = messages.ids and messages.ids[0] or False
# we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread
elif parent_id:
@@ -2047,7 +2052,8 @@ class MailThread(models.AbstractModel):
canned_response_ids = values.pop('canned_response_ids', False)
new_message = MailMessage.create(values)
values['canned_response_ids'] = canned_response_ids
- self._message_post_after_hook(new_message, values, model_description=model_description, mail_auto_delete=mail_auto_delete)
+ record = self.env['mail.thread'] if user_notification else self
+ record._message_post_after_hook(new_message, values, model_description=model_description, mail_auto_delete=mail_auto_delete)
return new_message
def _message_post_after_hook(self, message, msg_vals, model_description=False, mail_auto_delete=True):
@@ -2137,8 +2143,8 @@ class MailThread(models.AbstractModel):
return composer.send_mail()
def message_notify(self, partner_ids, body='', subject=False, **kwargs):
- """ Shortcut allowing to notify partners of messages not linked to
- any document. It pushes notifications on inbox or by email depending
+ """ Shortcut allowing to notify partners of messages that shouldn't be
+ displayed on a document. It pushes notifications on inbox or by email depending
on the user configuration, like other notifications. """
kw_author = kwargs.pop('author_id', False)
if kw_author:
@@ -2154,16 +2160,15 @@ class MailThread(models.AbstractModel):
'body': body,
'author_id': author.id,
'email_from': email_from,
- 'message_type': 'notification',
+ 'message_type': 'user_notification',
'partner_ids': partner_ids,
- 'model': False,
'subtype_id': self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note'),
'record_name': False,
'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from, records=None)[False],
'message_id': tools.generate_tracking_message_id('message-notify'),
}
msg_values.update(kwargs)
- return self.env['mail.thread'].message_post(**msg_values)
+ return self.message_post(**msg_values)
def _message_log(self, body='', subject=False, message_type='notification', **kwargs):
""" Shortcut allowing to post note on a document. It does not perform
diff --git a/addons/mail/static/src/js/models/messages/message.js b/addons/mail/static/src/js/models/messages/message.js
index 2e49975c9d9..73c4ece9025 100644
--- a/addons/mail/static/src/js/models/messages/message.js
+++ b/addons/mail/static/src/js/models/messages/message.js
@@ -368,7 +368,7 @@ var Message = AbstractMessage.extend(Mixins.EventDispatcherMixin, ServicesMixin
* @return {boolean}
*/
isLinkedToDocumentThread: function () {
- return !!(this._documentModel !== 'mail.channel' && this._documentID);
+ return !!(this._documentModel !== 'mail.channel' && this._documentID && this._type !== 'user_notification');
},
/**
* State whether the current user is the author of this message
diff --git a/addons/test_mail/tests/test_message_compose.py b/addons/test_mail/tests/test_message_compose.py
index 2397023db24..b756d52d510 100644
--- a/addons/test_mail/tests/test_message_compose.py
+++ b/addons/test_mail/tests/test_message_compose.py
@@ -205,17 +205,19 @@ class TestMessagePost(BaseFunctionalTest, MockEmails, TestRecipients):
def test_post_notify(self):
self.user_employee.write({'notification_type': 'inbox'})
- new_notification = self.env['mail.thread'].message_notify(
+ new_notification = self.test_record.message_notify(
subject='This should be a subject',
body='<p>You have received a notification</p>',
partner_ids=[(4, self.partner_1.id), (4, self.user_employee.partner_id.id)],
)
self.assertEqual(new_notification.subtype_id, self.env.ref('mail.mt_note'))
+ self.assertEqual(new_notification.message_type, 'user_notification')
self.assertEqual(new_notification.body, '<p>You have received a notification</p>')
self.assertEqual(new_notification.author_id, self.env.user.partner_id)
self.assertEqual(new_notification.email_from, formataddr((self.env.user.name, self.env.user.email)))
self.assertEqual(new_notification.needaction_partner_ids, self.partner_1 | self.user_employee.partner_id)
+ self.assertNotIn(new_notification, self.test_record.message_ids)
class TestComposer(BaseFunctionalTest, MockEmails, TestRecipients):
diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py
index 08ded3c4769..f87686cdf5d 100644
--- a/addons/test_mail/tests/test_performance.py
+++ b/addons/test_mail/tests/test_performance.py
@@ -485,7 +485,7 @@ class TestHeavyMailPerformance(TransactionCase):
'user_id': self.env.uid,
})
self.assertEqual(rec.message_partner_ids, self.partners | self.env.user.partner_id)
- with self.assertQueryCount(__system__=60, emp=75): # com runbot: 60 - 75 // test_mail only: 60 - 72
+ with self.assertQueryCount(__system__=61, emp=75): # com runbot: 60 - 75 // test_mail only: 60 - 72
rec.write({'user_id': self.user_portal.id})
self.assertEqual(rec.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id)
# write tracking message