Permalink
Browse files

[IMP] mail: allow tracking on groups protected fields

Purpose
=======

Currently fields with a `groups` attribute can't be tracked.
Otherwise changes would be visible by all in the chatter, including
users which normally don't have the access rights because they are
not members of `groups`.

Specification
=============

Filter tracking field values according to the `groups` attributes.
Users without the access rights should not see them.

If a message is only composed of tracking values and the user doesn't
have the rights to see them, an empty message should not be displayed.
  • Loading branch information...
Musvol committed Feb 8, 2019
1 parent 509d14a commit 6d1dfa1d48bb9c91b0a66882421427ec38d6dcc8
@@ -350,14 +350,16 @@ def _message_read_dict_postprocess(self, messages, message_tree):
message_to_tracking = dict()
tracking_tree = dict.fromkeys(tracking_values.ids, False)
for tracking in tracking_values:
message_to_tracking.setdefault(tracking.mail_message_id.id, list()).append(tracking.id)
tracking_tree[tracking.id] = {
'id': tracking.id,
'changed_field': tracking.field_desc,
'old_value': tracking.get_old_display_value()[0],
'new_value': tracking.get_new_display_value()[0],
'field_type': tracking.field_type,
}
groups = tracking.groups
if not groups or self.user_has_groups(groups):
message_to_tracking.setdefault(tracking.mail_message_id.id, list()).append(tracking.id)
tracking_tree[tracking.id] = {
'id': tracking.id,
'changed_field': tracking.field_desc,
'old_value': tracking.get_old_display_value()[0],
'new_value': tracking.get_new_display_value()[0],
'field_type': tracking.field_type,
}

# 4. Update message dictionaries
for message_dict in messages:
@@ -601,7 +601,7 @@ def _message_track(self, tracked_fields, initial):
and initial values, return a structure that is a tuple containing :
- a set of updated column names
- a list of changes (initial value, new value, column name, column info) """
- a list of ORM (0, 0, values) commands to create 'mail.tracking.value' """
self.ensure_one()
changes = set() # contains onchange tracked fields that changed
tracking_value_ids = []
@@ -35,6 +35,13 @@ class MailTracking(models.Model):

tracking_sequence = fields.Integer('Tracking field sequence', readonly=1, default=100, oldname='track_sequence')

groups = fields.Char(compute='_compute_groups')

def _compute_groups(self):
for tracking in self:
model = self.env[tracking.mail_message_id.model]
tracking.groups = model._fields[tracking.field].groups

@api.model
def create_tracking_values(self, initial_value, new_value, col_name, col_info, tracking_sequence):
tracked = True
@@ -67,9 +67,11 @@ def _notify_prepare_template_context(self, message, record, model_description=Fa

tracking = []
for tracking_value in self.env['mail.tracking.value'].sudo().search([('mail_message_id', '=', message.id)]):
tracking.append((tracking_value.field_desc,
tracking_value.get_old_display_value()[0],
tracking_value.get_new_display_value()[0]))
groups = tracking_value.groups
if not groups or self.user_has_groups(groups):
tracking.append((tracking_value.field_desc,
tracking_value.get_old_display_value()[0],
tracking_value.get_new_display_value()[0]))

is_discussion = message.subtype_id.id == self.env['ir.model.data'].xmlid_to_res_id('mail.mt_comment')

@@ -240,6 +240,17 @@ var AbstractMessage = Class.extend({
hasSubject: function () {
return false;
},
/**
* State whether this message is empty
*
* @return {boolean}
*/
isEmpty: function () {
return !this.hasTrackingValues() &&
!this.hasSubtypeDescription() &&
!this.hasAttachments() &&
!this.getBody();
},
/**
* By default, messages do not have any subtype description
*
@@ -255,7 +255,7 @@
@param {integer} [options.selectedMessageID]
-->
<t t-name="mail.widget.Thread.Message">
<div t-att-class="'o_thread_message ' + (message.getID() === options.selectedMessageID ? 'o_thread_selected_message ' : ' ') + (message.isDiscussion() or message.isNotification() ? ' o_mail_discussion ' : ' o_mail_not_discussion ')" t-att-data-message-id="message.getID()">
<div t-if="!message.isEmpty()" t-att-class="'o_thread_message ' + (message.getID() === options.selectedMessageID ? 'o_thread_selected_message ' : ' ') + (message.isDiscussion() or message.isNotification() ? ' o_mail_discussion ' : ' o_mail_not_discussion ')" t-att-data-message-id="message.getID()">
<div t-if="options.displayAvatars" class="o_thread_message_sidebar">
<t t-if="message.hasAuthor()">
<img t-if="displayAuthorMessages[message.getID()]"
@@ -64,6 +64,7 @@ class MailTestFull(models.Model):
customer_id = fields.Many2one('res.partner', 'Customer', tracking=2)
user_id = fields.Many2one('res.users', 'Responsible', tracking=1)
umbrella_id = fields.Many2one('mail.test', tracking=True)
protected = fields.Char(groups='base.group_erp_manager', tracking=True)

def _track_template(self, tracking):
res = super(MailTestFull, self)._track_template(tracking)
@@ -131,3 +131,19 @@ def test_message_tracking_sequence(self):
self.assertEqual(tracking_values[0].tracking_sequence, 1)
self.assertEqual(tracking_values[1].tracking_sequence, 2)
self.assertEqual(tracking_values[2].tracking_sequence, 100)

def test_message_format_track_groups(self):
self.record.sudo().write({'protected': 'X'})

msg_emp = self.record.message_ids.message_format()
msg_admin = self.record.message_ids.sudo(self.user_admin).message_format()
self.assertFalse(msg_emp[0].get('tracking_value_ids'), "should not have protected tracking values")
self.assertTrue(msg_admin[0].get('tracking_value_ids'), "should have protected tracking values")

def test_notify_track_groups(self):
self.record.sudo().write({'protected': 'X'})

msg_emp = self.partner_employee.sudo(self.user_employee)._notify_prepare_template_context(self.record.message_ids, self.record)
msg_admin = self.partner_admin.sudo(self.user_admin)._notify_prepare_template_context(self.record.message_ids, self.record)
self.assertFalse(msg_emp.get('tracking_values'), "should not have protected tracking values")
self.assertTrue(msg_admin.get('tracking_values'), "should have protected tracking values")
@@ -511,7 +511,7 @@ def test_complex_tracking_subscription_create(self):
customer_id = self.customer.id
user_id = self.user_portal.id

with self.assertQueryCount(__system__=161, emp=198): # com runbot: 161 - 198 // test_mail only: 161 - 192
with self.assertQueryCount(__system__=162, emp=199): # com runbot: 161 - 198 // test_mail only: 161 - 192
rec = self.env['mail.test.full'].create({
'name': 'Test',
'umbrella_id': umbrella_id,
@@ -540,7 +540,7 @@ def test_complex_tracking_subscription_subtype(self):
})
self.assertEqual(rec.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id)

with self.assertQueryCount(__system__=99, emp=120): # com runbot: 99 - 120 // test_mail only: 98 - 116
with self.assertQueryCount(__system__=100, emp=121): # com runbot: 99 - 120 // test_mail only: 98 - 116
rec.write({
'name': 'Test2',
'umbrella_id': self.umbrella.id,
@@ -578,7 +578,7 @@ def test_complex_tracking_subscription_write(self):
})
self.assertEqual(rec.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id)

with self.assertQueryCount(__system__=105, emp=126): # test_mail only: 105 - 122
with self.assertQueryCount(__system__=106, emp=127): # test_mail only: 105 - 122
rec.write({
'name': 'Test2',
'umbrella_id': umbrella_id,
@@ -353,7 +353,7 @@ class IrModelFields(models.Model):
domain = fields.Char(default="[]", help="The optional domain to restrict possible values for relationship fields, "
"specified as a Python expression defining a list of triplets. "
"For example: [('color','=','red')]")
groups = fields.Many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id')
groups = fields.Many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id') # CLEANME useless field (empty table)
selectable = fields.Boolean(default=True)
modules = fields.Char(compute='_in_modules', string='In Apps', help='List of modules in which the field is defined')
relation_table = fields.Char(help="Used for custom many2many fields to define a custom relation table name")

0 comments on commit 6d1dfa1

Please sign in to comment.