diff --git a/addons/mail/data/mail_data.xml b/addons/mail/data/mail_data.xml index 8b4c4ddcc65..8afde76abfd 100644 --- a/addons/mail/data/mail_data.xml +++ b/addons/mail/data/mail_data.xml @@ -68,5 +68,11 @@ Discussions + + Note + + + + diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index 7e191f9517d..44c0e254776 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -541,7 +541,7 @@ class Message(models.Model): count=count, access_rights_uid=access_rights_uid) # Non-employee see only messages with a subtype (aka, no internal logs) if not self.env['res.users'].has_group('base.group_user'): - args = ['&', ('subtype_id', '!=', False)] + list(args) + args = ['&', '&', ('subtype_id', '!=', False), ('subtype_id.internal', '=', False)] + list(args) # Perform a super with count as False, to have the ids, not a counter ids = super(Message, self)._search( args, offset=offset, limit=limit, order=order, @@ -620,7 +620,11 @@ class Message(models.Model): return # Non employees see only messages with a subtype (aka, not internal logs) if not self.env['res.users'].has_group('base.group_user'): - self._cr.execute('SELECT DISTINCT id FROM "%s" WHERE message_type = %%s AND subtype_id IS NULL AND id = ANY (%%s)' % (self._table), ('comment', self.ids,)) + self._cr.execute('''SELECT DISTINCT message.id, message.subtype_id, subtype.internal + FROM "%s" AS message + LEFT JOIN "mail_message_subtype" as subtype + ON message.subtype_id = subtype.id + WHERE message.message_type = %%s AND (message.subtype_id IS NULL OR subtype.internal IS TRUE) AND message.id = ANY (%%s)''' % (self._table), ('comment', self.ids,)) if self._cr.fetchall(): raise AccessError( _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % @@ -783,10 +787,16 @@ class Message(models.Model): self.ensure_one() # tde: not sure, just for testinh, will see partners_to_notify = self.env['res.partner'] - # all followers of the mail.message document have to be added as partners and notified if a subtype is defined (otherwise: log message) + # all followers of the mail.message document have to be added as partners and notified + # and filter to employees only if the subtype is internal if self.subtype_id and self.model and self.res_id: - followers = self.env['mail.followers'].sudo().search([('res_model', '=', self.model), ('res_id', '=', self.res_id)]) - partners_to_notify |= followers.filtered(lambda fol: self.subtype_id in fol.subtype_ids).mapped('partner_id') + followers = self.env['mail.followers'].sudo().search([ + ('res_model', '=', self.model), + ('res_id', '=', self.res_id) + ]).filtered(lambda fol: self.subtype_id in fol.subtype_ids) + if self.subtype_id.internal: + followers.filtered(lambda fol: fol.partner_id.user_ids and fol.partner_id.user_ids[0].has_group('base.group_user')) + partners_to_notify |= followers.mapped('partner_id') # remove me from notified partners, unless the message is written on my own wall if self.subtype_id and self.author_id and self.model == "res.partner" and self.res_id == self.author_id.id: diff --git a/addons/mail/models/mail_message_subtype.py b/addons/mail/models/mail_message_subtype.py index 26faa0a3e12..83d3f082159 100644 --- a/addons/mail/models/mail_message_subtype.py +++ b/addons/mail/models/mail_message_subtype.py @@ -22,9 +22,14 @@ class MailMessageSubtype(models.Model): 'Description', translate=True, help='Description that will be added in the message posted for this ' 'subtype. If void, the name will be added instead.') + internal = fields.Boolean( + 'Internal Only', + help='Messages with internal subtypes will be visible only by employees, aka members of base_user group') parent_id = fields.Many2one( 'mail.message.subtype', string='Parent', ondelete='set null', - help='Parent subtype, used for automatic subscription.') + help='Parent subtype, used for automatic subscription. This field is not' + 'correctly named. For example on a project, project subtypes have' + 'parent_id to task-related subtypes.') relation_field = fields.Char( 'Relation field', help='Field used to link the related model to the subtype model when ' diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 5b9755cafc8..a91b7c18768 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -336,8 +336,17 @@ class MailThread(models.AbstractModel): if view_type == 'form': doc = etree.XML(res['arch']) for node in doc.xpath("//field[@name='message_ids']"): + # the 'Log a note' button is employee only options = json.loads(node.get('options', '{}')) - options['display_log_button'] = self.env.user.sudo().has_group('base.group_user') + is_employee = self.env.user.has_group('base.group_user') + options['display_log_button'] = is_employee + if is_employee: + Subtype = self.env['mail.message.subtype'].sudo() + # fetch internal subtypes + internal_subtypes = Subtype.search_read([ + ('res_model', 'in', [False, self._name]), + ('internal', '=', True)], ['name', 'description', 'sequence']) + options['internal_subtypes'] = internal_subtypes node.set('options', json.dumps(options)) res['arch'] = etree.tostring(doc) return res @@ -1552,8 +1561,9 @@ class MailThread(models.AbstractModel): attachments, kwargs.pop('attachment_ids', []), model, self.ids and self.ids[0] or None) # 4: mail.message.subtype - subtype_id = False - if subtype: + subtype_id = kwargs.get('subtype_id', False) + if not subtype_id: + subtype = subtype or 'mt_note' if '.' not in subtype: subtype = 'mail.%s' % subtype subtype_id = self.env['ir.model.data'].xmlid_to_res_id(subtype) @@ -1607,8 +1617,10 @@ class MailThread(models.AbstractModel): # Post-process: subscribe author, update message_last_post if model and model != 'mail.thread' and self.ids and subtype_id: - # done with SUPERUSER_ID, because on some models users can post only with read access, not necessarily write access - self.sudo().write({'message_last_post': fields.datetime.now()}) + subtype_rec = self.env['mail.message.subtype'].sudo().browse(subtype_id) + if not subtype_rec.internal: + # done with SUPERUSER_ID, because on some models users can post only with read access, not necessarily write access + self.sudo().write({'message_last_post': fields.Datetime.now()}) if new_message.author_id and model and self.ids and message_type != 'notification' and not self._context.get('mail_create_nosubscribe'): self.message_subscribe([new_message.author_id.id]) return new_message diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv index c8f90dcaeac..c66045d6fe8 100644 --- a/addons/mail/security/ir.model.access.csv +++ b/addons/mail/security/ir.model.access.csv @@ -20,7 +20,7 @@ access_mail_alias_all,mail.alias.all,model_mail_alias,,1,0,0,0 access_mail_alias_user,mail.alias.user,model_mail_alias,base.group_user,1,1,1,0 access_mail_alias_system,mail.alias.system,model_mail_alias,base.group_system,1,1,1,1 access_mail_message_subtype_all,mail.message.subtype.all,model_mail_message_subtype,,1,0,0,0 -access_mail_message_subtype_system,mail.message.subtype.system,model_mail_message_subtype,base.group_system,1,1,1,1 +access_mail_message_subtype_user,mail.message.subtype.user,model_mail_message_subtype,,1,1,1,1 access_mail_tracking_value_all,mail.tracking.value.all,model_mail_tracking_value,,1,0,0,0 access_mail_tracking_value_portal,mail.tracking.value.portal,model_mail_tracking_value,base.group_portal,1,1,1,1 access_mail_tracking_value_user,mail.tracking.value.user,model_mail_tracking_value,base.group_user,1,1,1,1 diff --git a/addons/mail/security/mail_security.xml b/addons/mail/security/mail_security.xml index 5384e2aba95..2f07c659c95 100644 --- a/addons/mail/security/mail_security.xml +++ b/addons/mail/security/mail_security.xml @@ -38,6 +38,13 @@ + + mail.message.subtype: portal/public: read public subtypes + + [('internal', '=', False)] + + +