diff --git a/addons/base_action_rule/test_models.py b/addons/base_action_rule/test_models.py index fde48b815ad..5eb0dc1ff8b 100644 --- a/addons/base_action_rule/test_models.py +++ b/addons/base_action_rule/test_models.py @@ -30,5 +30,5 @@ class lead_test(osv.Model): def message_post(self, cr, uid, thread_id, body='', subject=None, message_type='notification', subtype=None, parent_id=False, attachments=None, context=None, **kwargs): pass - def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None): + def message_subscribe(self, cr, uid, ids, partner_ids=None, channel_ids=None, subtype_ids=None, force=True, context=None): pass diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index acd1a19f16f..aa89eb6cc90 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -1395,11 +1395,17 @@ class calendar_event(osv.Model): del context['default_date'] return super(calendar_event, self).message_post(cr, uid, thread_id, context=context, **kwargs) - def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None): - return super(calendar_event, self).message_subscribe(cr, uid, get_real_ids(ids), partner_ids, subtype_ids=subtype_ids, context=context) + def message_subscribe(self, cr, uid, ids, partner_ids=None, channel_ids=None, subtype_ids=None, force=True, context=None): + return super(calendar_event, self).message_subscribe( + cr, uid, get_real_ids(ids), + partner_ids=partner_ids, + channel_ids=channel_ids, + subtype_ids=subtype_ids, + force=force, + context=context) - def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None): - return super(calendar_event, self).message_unsubscribe(cr, uid, get_real_ids(ids), partner_ids, context=context) + def message_unsubscribe(self, cr, uid, ids, partner_ids=None, channel_ids=None, context=None): + return super(calendar_event, self).message_unsubscribe(cr, uid, get_real_ids(ids), partner_ids=partner_ids, channel_ids=channel_ids, context=context) def do_sendmail(self, cr, uid, ids, context=None): for event in self.browse(cr, uid, ids, context): diff --git a/addons/hr_evaluation/hr_evaluation.py b/addons/hr_evaluation/hr_evaluation.py index e9998279379..e7d181f1e2a 100644 --- a/addons/hr_evaluation/hr_evaluation.py +++ b/addons/hr_evaluation/hr_evaluation.py @@ -240,8 +240,11 @@ class hr_evaluation(osv.Model): def write(self, cr, uid, ids, vals, context=None): if vals.get('employee_id'): employee_id = self.pool.get('hr.employee').browse(cr, uid, vals.get('employee_id'), context=context) + # TDE FIXME: use add_command if employee_id.parent_id and employee_id.parent_id.user_id: - vals['message_follower_ids'] = [(4, employee_id.parent_id.user_id.partner_id.id)] + message_follower_ids = vals.get('message_follower_ids') or [] # webclient can send None or False + message_follower_ids += self.env['mail.followers']._add_follower_command(self._name, ids, {employee_id.parent_id.user_id.partner_id.id: None}, {}, force=True)[0] + vals['message_follower_ids'] = message_follower_ids if 'date' in vals: new_vals = {'deadline': vals.get('date')} obj_hr_eval_iterview = self.pool.get('hr.evaluation.interview') diff --git a/addons/mail/controllers/main.py b/addons/mail/controllers/main.py index 8b72fc74106..a9c6d98eaac 100644 --- a/addons/mail/controllers/main.py +++ b/addons/mail/controllers/main.py @@ -48,12 +48,14 @@ class MailController(http.Controller): def read_followers(self, follower_ids): result = [] is_editable = request.env.user.has_group('base.group_no_one') - for follower in request.env['res.partner'].browse(follower_ids): + for follower in request.env['mail.followers'].browse(follower_ids): result.append({ 'id': follower.id, - 'name': follower.name, + 'name': follower.partner_id.name or follower.channel_id.name, + 'res_model': 'res.partner' if follower.partner_id else 'mail.channel', + 'res_id': follower.partner_id.id or follower.channel_id.id, 'is_editable': is_editable, - 'is_uid': request.env.user.partner_id == follower, + 'is_uid': request.env.user.partner_id == follower.partner_id, }) return result diff --git a/addons/mail/data/mail_channel_demo.xml b/addons/mail/data/mail_channel_demo.xml index d14f65acd9a..3a6fd6bc6ad 100644 --- a/addons/mail/data/mail_channel_demo.xml +++ b/addons/mail/data/mail_channel_demo.xml @@ -7,23 +7,14 @@ Best Sales Practices Discussion about best sales practices and deals. - - - Board meetings Board meetings, budgets, strategic plans - - - R&D Research and development discussion group - - - diff --git a/addons/mail/models/mail_followers.py b/addons/mail/models/mail_followers.py index 59d6d5d4254..2e71ff1433c 100644 --- a/addons/mail/models/mail_followers.py +++ b/addons/mail/models/mail_followers.py @@ -23,11 +23,87 @@ class Followers(models.Model): res_id = fields.Integer( 'Related Document ID', select=1, help='Id of the followed resource') partner_id = fields.Many2one( - 'res.partner', string='Related Partner', ondelete='cascade', required=True, select=1) + 'res.partner', string='Related Partner', ondelete='cascade', select=1) + channel_id = fields.Many2one( + 'mail.channel', string='Listener', ondelete='cascade', select=1) subtype_ids = fields.Many2many( 'mail.message.subtype', string='Subtype', help="Message subtypes followed, meaning subtypes that will be pushed onto the user's Wall.") + @api.model + def _add_follower_command(self, res_model, res_ids, partner_data, channel_data, force=True): + """ Please upate me + :param force: if True, delete existing followers before creating new one + using the subtypes given in the parameters + """ + force_mode = force or (all(data for data in partner_data.values()) and all(data for data in channel_data.values())) + generic = [] + specific = {} + existing = {} # {res_id: follower_ids} + p_exist = {} # {partner_id: res_ids} + c_exist = {} # {channel_id: res_ids} + + followers = self.sudo().search([ + '&', + '&', ('res_model', '=', res_model), ('res_id', 'in', res_ids), + '|', ('partner_id', 'in', partner_data.keys()), ('channel_id', 'in', channel_data.keys())]) + + if force_mode: + followers.unlink() + else: + for follower in followers: + existing.setdefault(follower.res_id, list()).append(follower) + if follower.partner_id: + p_exist.setdefault(follower.partner_id.id, list()).append(follower.res_id) + if follower.channel_id: + c_exist.setdefault(follower.channel_id.id, list()).append(follower.res_id) + + default_subtypes = self.env['mail.message.subtype'].search([ + ('default', '=', True), + '|', ('res_model', '=', res_model), ('res_model', '=', False)]) + + if force_mode: + for pid, data in partner_data.iteritems(): + if not data: + partner_data[pid] = default_subtypes.ids + for cid, data in channel_data.iteritems(): + if not data: + channel_data[cid] = default_subtypes.ids + + # create new followers, batch ok + gen_new_pids = [pid for pid in partner_data.keys() if pid not in p_exist] + gen_new_cids = [cid for cid in channel_data.keys() if cid not in c_exist] + for pid in gen_new_pids: + generic.append([0, 0, {'res_model': res_model, 'partner_id': pid, 'subtype_ids': [(6, 0, partner_data.get(pid) or default_subtypes.ids)]}]) + for cid in gen_new_cids: + generic.append([0, 0, {'res_model': res_model, 'channel_id': cid, 'subtype_ids': [(6, 0, channel_data.get(cid) or default_subtypes.ids)]}]) + + # create new followers, each document at a time because of existing followers to avoid erasing + if not force_mode: + for res_id in res_ids: + command = [] + doc_followers = existing.get(res_id, list()) + + new_pids = set(partner_data.keys()) - set([sub.partner_id.id for sub in doc_followers if sub.partner_id]) - set(gen_new_pids) + new_cids = set(channel_data.keys()) - set([sub.channel_id.id for sub in doc_followers if sub.channel_id]) - set(gen_new_cids) + + # subscribe new followers + for new_pid in new_pids: + command.append((0, 0, { + 'res_model': res_model, + 'partner_id': new_pid, + 'subtype_ids': [(6, 0, partner_data.get(new_pid) or default_subtypes.ids)], + })) + for new_cid in new_cids: + command.append((0, 0, { + 'res_model': res_model, + 'channel_id': new_cid, + 'subtype_ids': [(6, 0, channel_data.get(new_cid) or default_subtypes.ids)], + })) + if command: + specific[res_id] = command + return generic, specific + # # Modifying followers change access rights to individual documents. As the # cache may contain accessible/inaccessible data, one has to refresh it. @@ -50,7 +126,10 @@ class Followers(models.Model): self.invalidate_cache() return res - _sql_constraints = [('mail_followers_res_partner_res_model_id_uniq', 'unique(res_model,res_id,partner_id)', 'Error, a partner cannot follow twice the same object.')] + _sql_constraints = [ + ('mail_followers_res_partner_res_model_id_uniq', 'unique(res_model,res_id,partner_id)', 'Error, a partner cannot follow twice the same object.'), + ('mail_followers_res_channel_res_model_id_uniq', 'unique(res_model,res_id,channel_id)', 'Error, a channel cannot follow twice the same object.'), + ] class Notification(models.Model): diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 661431f6fc5..ecca6887b38 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -66,11 +66,16 @@ class MailThread(models.AbstractModel): _mail_post_access = 'write' # access required on the document to post on it _mail_mass_mailing = False # enable mass mailing on this model - message_is_follower = fields.Boolean('Is Follower', compute='_get_followers', search='_search_is_follower') - message_follower_ids = fields.Many2many( - comodel_name='res.partner', string='Followers', - inverse='_set_followers', - compute='_get_followers', search='_search_followers', type='many2many') + message_is_follower = fields.Boolean('Is Follower', compute='_compute_is_follower') + message_follower_ids = fields.One2many( + 'mail.followers', 'res_id', string='Followers', + domain=lambda self: [('res_model', '=', self._name)]) + message_partner_ids = fields.Many2many( + comodel_name='res.partner', string='Followers (Partners)', + compute='_get_followers', search='_search_follower_partners') + message_channel_ids = fields.Many2many( + comodel_name='mail.channel', string='Followers (Channels)', + 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, @@ -83,72 +88,45 @@ class MailThread(models.AbstractModel): 'Unread Messages', compute='_get_message_unread', help="Number of unread messages") - @api.multi + @api.one + @api.depends('message_follower_ids') def _get_followers(self): - res = dict.fromkeys(self.ids, self.env['res.partner']) - followers = self.env['mail.followers'].sudo().search([('res_model', '=', self._name), ('res_id', 'in', self.ids)]) - for follower in followers: - res[follower.res_id] |= follower.partner_id - for record in self: - if not record.id: - continue - record.message_follower_ids = res[record.id] - record.message_is_follower = self.env.user.partner_id in record.message_follower_ids - - @api.multi - def _set_followers(self): - # read the old set of followers, and determine the new set of followers - old = self.env['mail.followers'].sudo().search([('res_model', '=', self._name), ('res_id', 'in', self.ids)]).mapped('partner_id') - new = self.env['res.partner'] - Partner = self.env['res.partner'] - - old_style_commands = self._fields['message_follower_ids'].convert_to_write(self.message_follower_ids) - for command in old_style_commands: - if command[0] == 0: - new |= Partner.create(command[2]) - elif command[0] == 1: - partner = Partner.browse(command[1]) - partner.write(command[2]) - new |= partner - elif command[0] == 2: - partner = Partner.browse(command[1]) - new -= partner - partner.unlink() - elif command[0] == 3: - new -= Partner.browse(command[1]) - elif command[0] == 4: - new |= Partner.browse(command[1]) - elif command[0] == 5: - new = self.env['res.partner'] - elif command[0] == 6: - new = self.env['res.partner'].browse(command[2]) - - # remove partners that are no longer followers - self.message_unsubscribe((old-new).ids) - # add new followers - self.message_subscribe((new-old).ids) + self.message_partner_ids = self.message_follower_ids.mapped('partner_id') + self.message_channel_ids = self.message_follower_ids.mapped('channel_id') @api.model - def _search_followers(self, operator, operand): + def _search_follower_partners(self, operator, operand): """Search function for message_follower_ids Do not use with operator 'not in'. Use instead message_is_followers """ - # TOFIX make it work with not in - assert operator != "not in", "Do not search message_follower_ids with 'not in'" - followers = self.env['mail.followers'].sudo().search([('res_model', '=', self._name), ('partner_id', operator, operand)]) + followers = self.env['mail.followers'].sudo().search([ + ('res_model', '=', self._name), + ('partner_id', operator, operand)]) return [('id', 'in', followers.mapped('res_id'))] @api.model - def _search_is_follower(self, operator, operand): - """Search function for message_is_follower""" - user_pid = self.env.user.partner_id.id - if (operator == '=' and operand) or (operator == '!=' and not operand): # is a follower - followers = self.env['mail.followers'].sudo().search([('res_model', '=', self._name), ('partner_id', '=', user_pid)]) - return [('id', 'in', followers.mapped('res_id'))] - else: # is not a follower or unknown domain - followers = self.env['mail.followers'].sudo().search([('res_model', '=', self._name), ('partner_id', '=', user_pid)]) - return [('id', 'not in', followers.mapped('res_id'))] + def _search_follower_channels(self, operator, operand): + """Search function for message_follower_ids + + Do not use with operator 'not in'. Use instead message_is_followers + """ + followers = self.env['mail.followers'].sudo().search([ + ('res_model', '=', self._name), + ('channel_id', operator, operand)]) + return [('id', 'in', followers.mapped('res_id'))] + + @api.multi + @api.depends('message_follower_ids') + def _compute_is_follower(self): + followers = self.env['mail.followers'].sudo().search([ + ('res_model', '=', self._name), + ('res_id', 'in', self.ids), + ('partner_id', '=', self.env.user.partner_id.id), + ]) + following_ids = followers.mapped('res_id') + for record in self: + record.message_is_follower = record.id in following_ids @api.multi def _get_message_unread(self): @@ -188,7 +166,7 @@ class MailThread(models.AbstractModel): # subscribe uid unless asked not to if not self._context.get('mail_create_nosubscribe'): message_follower_ids = values.get('message_follower_ids') or [] # webclient can send None or False - message_follower_ids.append([4, self.env.user.partner_id.id]) + message_follower_ids += self.env['mail.followers']._add_follower_command(self._name, [], {self.env.user.partner_id.id: None}, {}, force=True)[0] values['message_follower_ids'] = message_follower_ids thread = super(MailThread, self).create(values) @@ -765,7 +743,7 @@ class MailThread(models.AbstractModel): obj = record_set[0] else: obj = self.env[alias.alias_parent_model_id.model].browse(alias.alias_parent_thread_id) - if not author_id or author_id not in [fol.id for fol in obj.message_follower_ids]: + if not author_id or author_id not in [fol.id for fol in obj.message_partner_ids]: _warn('alias %s restricted to internal followers, skipping' % alias.alias_name) _create_bounce_email() return False @@ -1296,7 +1274,7 @@ class MailThread(models.AbstractModel): partner = self.env['res.partner'].sudo().browse([partner_info['partner_id']])[0] if email and email in [val[1] for val in result[self.ids[0]]]: # already existing email -> skip return result - if partner and partner in self.message_follower_ids: # recipient already in the followers -> skip + if partner and partner in self.message_partner_ids: # recipient already in the followers -> skip return result if partner and partner.id in [val[0] for val in result[self.ids[0]]]: # already existing partner ID -> skip return result @@ -1339,8 +1317,8 @@ class MailThread(models.AbstractModel): followers = self.env['res.partner'] if res_model and res_id: record = self.env[res_model].browse(res_id) - if hasattr(record, 'message_follower_ids'): - followers = record.message_follower_ids + if hasattr(record, 'message_partner_ids'): + followers = record.message_partner_ids Partner = self.env['res.partner'].sudo() partner_ids = [] @@ -1601,16 +1579,18 @@ class MailThread(models.AbstractModel): return result @api.multi - def message_subscribe(self, partner_ids, subtype_ids=None): + def message_subscribe(self, partner_ids=None, channel_ids=None, subtype_ids=None, force=True): """ Add partners to the records followers. """ # not necessary for computation, but saves an access right check - if not partner_ids: + if not partner_ids and not channel_ids: return True + if partner_ids is None: + partner_ids = [] + if channel_ids is None: + channel_ids = [] - Followers = self.env['mail.followers'] - Subtype = self.env['mail.message.subtype'] - - if set(partner_ids) == set([self.env.user.partner_id.id]): + # TDE CHECK THIS + if not channel_ids and partner_ids and set(partner_ids) == set([self.env.user.partner_id.id]): try: self.check_access_rights('read') self.check_access_rule('read') @@ -1620,38 +1600,14 @@ class MailThread(models.AbstractModel): self.check_access_rights('write') self.check_access_rule('write') - existing_pids_dict = dict.fromkeys(self.ids, self.env['res.partner']) - followers = Followers.sudo().search([ - '&', '&', - ('res_model', '=', self._name), - ('res_id', 'in', self.ids), - ('partner_id', 'in', partner_ids)]) - for follower in followers: - existing_pids_dict[follower.res_id] |= follower.partner_id - - # subtype_ids specified: update already subscribed partners - if subtype_ids and followers: - followers.write({'subtype_ids': [(6, 0, subtype_ids)]}) - # subtype_ids not specified: do not update already subscribed partner, fetch default subtypes for new partners - if subtype_ids is None: - subtype_ids = Subtype.search([ - ('default', '=', True), - '|', - ('res_model', '=', self._name), - ('res_model', '=', False)]).ids - - for record in self: - existing_pids = existing_pids_dict[record.id] - new_partners = self.env['res.partner'].browse(partner_ids) - existing_pids - - # subscribe new followers - for new_partner in new_partners: - Followers.sudo().create({ - 'res_model': self._name, - 'res_id': record.id, - 'partner_id': new_partner.id, - 'subtype_ids': [(6, 0, subtype_ids)]}) + partner_data = dict((pid, subtype_ids) for pid in partner_ids) + channel_data = dict((cid, subtype_ids) for cid in channel_ids) + gen, part = self.env['mail.followers']._add_follower_command(self._name, self.ids, partner_data, channel_data, force=force) + self.sudo().write({'message_follower_ids': gen}) + for record in self.filtered(lambda self: self.id in part): + record.write(part[record.id]) + self.invalidate_cache() return True @api.multi @@ -1667,13 +1623,13 @@ class MailThread(models.AbstractModel): return result @api.multi - def message_unsubscribe(self, partner_ids): + def message_unsubscribe(self, partner_ids=None, channel_ids=None): """ Remove partners from the records followers. """ # not necessary for computation, but saves an access right check - if not partner_ids: + if not partner_ids and not channel_ids: return True user_pid = self.env.user.partner_id.id - if set(partner_ids) == set([user_pid]): + if not channel_ids and set(partner_ids) == set([user_pid]): self.check_access_rights('read') self.check_access_rule('read') else: @@ -1682,7 +1638,9 @@ class MailThread(models.AbstractModel): self.env['mail.followers'].sudo().search([ ('res_model', '=', self._name), ('res_id', 'in', self.ids), - ('partner_id', 'in', partner_ids) + '|', + ('partner_id', 'in', partner_ids or []), + ('channel_id', 'in', channel_ids or []) ]).unlink() @api.model @@ -1808,7 +1766,7 @@ class MailThread(models.AbstractModel): for pid, subtypes in new_followers.items(): subtypes = list(subtypes) if subtypes is not None else None - self.message_subscribe([pid], subtypes) + self.message_subscribe([pid], subtype_ids=subtypes) self._message_auto_subscribe_notify(user_pids) diff --git a/addons/mail/models/res_users.py b/addons/mail/models/res_users.py index 34d4d73092a..c3edd006130 100644 --- a/addons/mail/models/res_users.py +++ b/addons/mail/models/res_users.py @@ -121,7 +121,7 @@ class Users(models.Model): def message_update(self, msg_dict, update_vals=None): return True - def message_subscribe(self, partner_ids, subtype_ids=None): + def message_subscribe(self, partner_ids=None, channel_ids=None, subtype_ids=None, force=True): return True @api.cr_uid_context diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv index 701bbffbeca..7193db4a9a2 100644 --- a/addons/mail/security/ir.model.access.csv +++ b/addons/mail/security/ir.model.access.csv @@ -7,8 +7,8 @@ access_mail_mail_portal,mail.mail.portal,model_mail_mail,base.group_portal,1,1,1 access_mail_mail_user,mail.mail.user,model_mail_mail,base.group_user,1,1,1,0 access_mail_mail_system,mail.mail.system,model_mail_mail,base.group_system,1,1,1,1 access_mail_followers_all,mail.followers.all,model_mail_followers,,1,0,0,0 -access_mail_followers_portal,mail.followers.portal,model_mail_followers,base.group_portal,1,1,0,0 -access_mail_followers_user,mail.followers.user,model_mail_followers,base.group_user,1,1,0,0 +access_mail_followers_portal,mail.followers.portal,model_mail_followers,base.group_portal,1,1,1,0 +access_mail_followers_user,mail.followers.user,model_mail_followers,base.group_user,1,1,1,0 access_mail_followers_system,mail.followers.system,model_mail_followers,base.group_system,1,1,1,1 access_mail_notification_all,mail.notification.all,model_mail_notification,,1,0,0,0 access_mail_notification_portal,mail.notification.portal,model_mail_notification,base.group_portal,1,1,1,0 diff --git a/addons/mail/security/mail_security.xml b/addons/mail/security/mail_security.xml index c05d434c789..642975ea7a5 100644 --- a/addons/mail/security/mail_security.xml +++ b/addons/mail/security/mail_security.xml @@ -7,17 +7,18 @@ Mail.channel: access only public and joined groups - ['|', '|', ('public', '=', 'public'), ('message_follower_ids', 'in', [user.partner_id.id]), '&', ('public','=','groups'), ('group_public_id','in', [g.id for g in user.groups_id])] + ['|', '|', ('public', '=', 'public'), ('message_partner_ids', 'in', [user.partner_id.id]), '&', ('public','=','groups'), ('group_public_id','in', [g.id for g in user.groups_id])] - mail.followers: read and write its own entries + mail.followers: write its own entries [('partner_id', '=', user.partner_id.id)] + diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index 15584c518b5..7f916dd3df9 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -77,7 +77,7 @@ var Followers = form_common.AbstractField.extend({ self.do_follow(); } else { - self.do_unfollow(); + self.do_unfollow([session.uid]); } }); @@ -145,15 +145,10 @@ var Followers = form_common.AbstractField.extend({ }, on_remove_follower: function (event) { - var partner_id = $(event.target).data('id'); - var name = $(event.target).parent().find("a").html(); - if (confirm(_.str.sprintf(_t("Warning! \n %s won't be notified of any email or discussion on this document. Do you really want to remove him from the followers ?"), name))) { - var context = new data.CompoundContext(this.build_context(), {}); - return this.ds_model.call('message_unsubscribe', [[this.view.datarecord.id], - [partner_id], - context]) - .then(this.proxy('read_value')); - } + var res_model = $(event.target).parent().find('a').data('res-model'); + var res_id = $(event.target).parent().find('a').data('res-id'); + if (res_model == 'res.partner') { return this.do_unfollow(undefined, [res_id], undefined); } + else { return this.do_unfollow(undefined, undefined, [res_id]); } }, on_follower_clicked: function (event) { @@ -245,19 +240,13 @@ var Followers = form_common.AbstractField.extend({ // truncate number of displayed followers _(this.followers).each(function (record) { - var partner = { - 'id': record['id'], - 'name': record['name'], - 'is_uid': record['is_uid'], - 'is_editable': record['is_editable'], - 'avatar_url': mail_utils.get_image(session, 'res.partner', 'image_small', record['id']), - } - - $(qweb.render('mail_followers_partner', {'record': partner, 'widget': self})) - .appendTo(node_user_list); + $(qweb.render('mail_followers_partner', { + 'record': _.extend(record, {'avatar_url': mail_utils.get_image(session, record['res_model'], 'image_small', record['id'])}), + 'widget': self}) + ).appendTo(node_user_list); // On mouse-enter it will show the edit_subtype pencil. - if (partner.is_editable) { + if (record.is_editable) { self.$('.o_timeline_follower_list').on('mouseenter mouseleave', function(e) { self.$('.o_timeline_edit_subtype') .toggleClass('oe_hidden', e.type == 'mouseleave'); @@ -358,26 +347,33 @@ var Followers = form_common.AbstractField.extend({ $(record).attr('checked', 'checked'); }); }, - - do_unfollow: function (user_pid) { - if (confirm(_t("Warning! \nYou won't be notified of any email or discussion on this document. Do you really want to unfollow this document ?"))) { + + do_unfollow: function (user_ids, partner_ids, channel_ids) { + if (confirm(_t("Warning! \n If you remove a follower, he won't be notified of any email or discussion on this document. Do you really want to remove this follower ?"))) { _(this.$('.o_timeline_msg_subtype_check')).each(function (record) { $(record).attr('checked',false); }); - var action_unsubscribe = 'message_unsubscribe_users'; this.$('.o_timeline_subtype_list > .dropdown-toggle').attr('disabled', true); - var follower_ids = [session.uid]; - - if (user_pid) { - action_unsubscribe = 'message_unsubscribe'; - follower_ids = [user_pid]; - } - var context = new data.CompoundContext(this.build_context(), {}); - return this.ds_model.call(action_unsubscribe, [[this.view.datarecord.id], - follower_ids, context]) - .then(this.proxy('read_value')); + + if (partner_ids || channel_ids) { + return this.ds_model.call( + 'message_unsubscribe', [ + [this.view.datarecord.id], + partner_ids, + channel_ids, + context] + ).then(this.proxy('read_value')); + } + else { + return this.ds_model.call( + 'message_unsubscribe_users', [ + [this.view.datarecord.id], + user_ids, + context] + ).then(this.proxy('read_value')); + } } return false; }, @@ -404,7 +400,7 @@ var Followers = form_common.AbstractField.extend({ }); if (!checklist.length) { - if (!this.do_unfollow(user_pid)) { + if (!this.do_unfollow(undefined, [user_pid], undefined)) { $(event.target).attr("checked", "checked"); } else { diff --git a/addons/mail/static/src/xml/mail_followers.xml b/addons/mail/static/src/xml/mail_followers.xml index e38fa8f480d..dd6d9883abd 100644 --- a/addons/mail/static/src/xml/mail_followers.xml +++ b/addons/mail/static/src/xml/mail_followers.xml @@ -42,14 +42,17 @@ - + t-att-title="record.name" + t-att-data-res-model="record.res_model" + t-att-data-res-id="record.res_id" + t-att-data-id="record.id"> + + title="Remove this follower"/> @@ -66,7 +69,7 @@