diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index 626edb66c31..a740d8a2ac9 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -6,7 +6,7 @@ import uuid from odoo import api, fields, models, tools, _ from odoo.addons.http_routing.models.ir_http import slug from odoo.addons.gamification.models.gamification_karma_rank import KarmaError -from odoo.exceptions import UserError +from odoo.exceptions import UserError, AccessError from odoo.osv import expression @@ -73,6 +73,23 @@ class ChannelUsersRelation(models.Model): def _post_completion_hook(self): pass + def unlink(self): + """ + Override unlink method : + Remove attendee from a channel, then also remove slide.slide.partner related to. + """ + removed_slide_partner_domain = [] + for channel_partner in self: + # find all slide link to the channel and the partner + removed_slide_partner_domain = expression.OR([ + removed_slide_partner_domain, + [('partner_id', '=', channel_partner.partner_id.id), + ('slide_id', 'in', channel_partner.channel_id.slide_ids.ids)] + ]) + if removed_slide_partner_domain: + self.env['slide.slide.partner'].search(removed_slide_partner_domain).unlink() + return super(ChannelUsersRelation, self).unlink() + class Channel(models.Model): """ A channel is a container of slides. """ @@ -384,23 +401,13 @@ class Channel(models.Model): # --------------------------------------------------------- def action_redirect_to_members(self, state=None): - action = self.env.ref('website_slides.res_partner_action_slide_channel').read()[0] - action['context'] = {'active_test': False} - if state == 'running': - action['domain'] = [('id', 'in', self.env['slide.channel.partner'].sudo().search([ - ('channel_id', 'in', self.ids), - ('completed', '=', False) - ]).mapped('partner_id').ids)] - elif state == 'completed': - action['domain'] = [('id', 'in', self.env['slide.channel.partner'].sudo().search([ - ('channel_id', 'in', self.ids), - ('completed', '=', True) - ]).mapped('partner_id').ids)] - else: - action['domain'] = [('id', 'in', self.mapped('partner_ids').ids)] + action = self.env.ref('website_slides.slide_channel_partner_action').read()[0] + action['domain'] = [('channel_id', 'in', self.ids)] if len(self) == 1: action['display_name'] = _('Attendees of %s') % self.name action['context'] = {'active_test': False, 'default_channel_id': self.id} + if state: + action['domain'] += [('completed', '=', state == 'completed')] return action def action_redirect_to_running_members(self): @@ -479,28 +486,18 @@ class Channel(models.Model): def _remove_membership(self, partner_ids): """ Unlink (!!!) the relationships between the passed partner_ids - and the channels and their slides. """ + and the channels and their slides (done in the unlink of slide.channel.partner model). """ if not partner_ids: raise ValueError("Do not use this method with an empty partner_id recordset") - removed_slide_partner_domain = [] removed_channel_partner_domain = [] for channel in self: - removed_slide_partner_domain = expression.OR([ - removed_slide_partner_domain, - [('partner_id', 'in', partner_ids.ids), - ('slide_id', 'in', channel.slide_ids.ids)] - ]) - removed_channel_partner_domain = expression.OR([ removed_channel_partner_domain, [('partner_id', 'in', partner_ids.ids), ('channel_id', '=', channel.id)] ]) - if removed_slide_partner_domain: - self.env['slide.slide.partner'].sudo().search(removed_slide_partner_domain).unlink() - if removed_channel_partner_domain: self.env['slide.channel.partner'].sudo().search(removed_channel_partner_domain).unlink() diff --git a/addons/website_slides/tests/test_security.py b/addons/website_slides/tests/test_security.py index b1e6a979a9d..0ac21ba813d 100644 --- a/addons/website_slides/tests/test_security.py +++ b/addons/website_slides/tests/test_security.py @@ -123,6 +123,40 @@ class TestAccess(common.SlidesCase): self.slide.with_user(self.user_public).read(['name']) +@tagged('functional', 'security') +class TestRemoveMembership(common.SlidesCase): + + def setUp(self): + super(TestRemoveMembership, self).setUp() + self.channel_partner = self.env['slide.channel.partner'].create({ + 'channel_id': self.channel.id, + 'partner_id': self.customer.id, + }) + + self.slide_partner = self.env['slide.slide.partner'].create({ + 'slide_id': self.slide.id, + 'channel_id': self.channel.id, + 'partner_id': self.customer.id + }) + + def test_security_unlink(self): + # Only the publisher can unlink channel_partner (and slide_partner by extension) + with self.assertRaises(AccessError): + self.channel_partner.with_user(self.user_public).unlink() + with self.assertRaises(AccessError): + self.channel_partner.with_user(self.user_portal).unlink() + with self.assertRaises(AccessError): + self.channel_partner.with_user(self.user_emp).unlink() + + def test_slide_partner_remove(self): + id_slide_partner = self.slide_partner.id + id_channel_partner = self.channel_partner.id + self.channel_partner.with_user(self.user_publisher).unlink() + self.assertFalse(self.env['slide.channel.partner'].search([('id', '=', '%d' % id_channel_partner)])) + # Slide(s) related to the channel and the partner is unlink too. + self.assertFalse(self.env['slide.slide.partner'].search([('id', '=', '%d' % id_slide_partner)])) + + @tagged('functional') class TestAccessFeatures(common.SlidesCase): diff --git a/addons/website_slides/views/res_partner_views.xml b/addons/website_slides/views/res_partner_views.xml index 8a202cb0311..4f4f9d3c425 100644 --- a/addons/website_slides/views/res_partner_views.xml +++ b/addons/website_slides/views/res_partner_views.xml @@ -28,9 +28,4 @@ - - Attendees - res.partner - kanban,tree,form - diff --git a/addons/website_slides/views/slide_channel_partner_views.xml b/addons/website_slides/views/slide_channel_partner_views.xml index fb5901d15da..05720fcfb03 100644 --- a/addons/website_slides/views/slide_channel_partner_views.xml +++ b/addons/website_slides/views/slide_channel_partner_views.xml @@ -29,8 +29,16 @@ +