diff --git a/addons/gamification/data/goal_base.xml b/addons/gamification/data/goal_base.xml index af96a4806cf74545a9994ad9dc3dc4c3971769e0..3289073cb132f781e4d4c86b566de6e516f7754b 100644 --- a/addons/gamification/data/goal_base.xml +++ b/addons/gamification/data/goal_base.xml @@ -280,15 +280,6 @@ Thank you, <field name="action_id" eval="ref('action_new_simplified_res_users')" /> </record> - <record model="gamification.goal.definition" id="definition_nbr_following"> - <field name="name">Mail Group Following</field> - <field name="description">Follow mail.channels to receive news</field> - <field name="computation_mode">python</field> - <field name="compute_code">result = env['mail.followers'].search_count([('res_model', '=', 'mail.channel'), ('partner_id', '=', object.user_id.partner_id.id)])</field> - <field name="action_id" eval="ref('mail.mail_channel_action_view')" /> - </record> - - <!-- challenges --> <record model="gamification.challenge" id="challenge_base_discover"> <field name="name">Complete your Profile</field> diff --git a/addons/gamification/views/goal.xml b/addons/gamification/views/goal.xml index 7301891f705206785ca56cba5367f353150267b0..1f43e81fc6ec33f7675003477c66f12356c45732 100644 --- a/addons/gamification/views/goal.xml +++ b/addons/gamification/views/goal.xml @@ -249,7 +249,7 @@ attrs="{'invisible':[('computation_mode','!=','sum')], 'required':[('computation_mode','=','sum')]}"/> <field name="field_date_id" class="oe_inline" attrs="{'invisible':[('computation_mode','not in',('sum', 'count'))]}"/> <field name="domain" attrs="{'invisible':[('computation_mode','not in',('sum', 'count'))], 'required':[('computation_mode','in',('sum', 'count'))]}" class="oe_inline"/> - <field name="compute_code" attrs="{'invisible':[('computation_mode','!=','python')], 'required':[('computation_mode','=','python')]}" placeholder="e.g. result = env['mail.followers'].search_count([('res_model', '=', 'mail.channel'), ('partner_id', '=', object.user_id.partner_id.id)])"/> + <field name="compute_code" attrs="{'invisible':[('computation_mode','!=','python')], 'required':[('computation_mode','=','python')]}"/> <field name="condition" widget="radio"/> </group> <group string="Optimisation" name="optimisation" attrs="{'invisible': [('computation_mode', '!=', 'count')]}"> diff --git a/addons/mail/controllers/main.py b/addons/mail/controllers/main.py index 2dbc9c09288fa56f1a3535a838ef3d0907b5dcc3..b8a2387dbed0f5ad9639558864bf5835ce77efa6 100644 --- a/addons/mail/controllers/main.py +++ b/addons/mail/controllers/main.py @@ -141,55 +141,66 @@ class MailController(http.Controller): return True @http.route('/mail/read_followers', type='json', auth='user') - def read_followers(self, follower_ids, res_model): - followers = [] - # When editing the followers, the "pencil" icon that leads to the edition of subtypes - # should be always be displayed and not only when "debug" mode is activated. - is_editable = True - partner_id = request.env.user.partner_id - follower_id = None + def read_followers(self, follower_ids): + request.env['mail.followers'].check_access_rights("read") follower_recs = request.env['mail.followers'].sudo().browse(follower_ids) res_ids = follower_recs.mapped('res_id') + res_models = set(follower_recs.mapped('res_model')) + if len(res_models) > 1: + raise AccessError(_("Can't read followers with different targeted model")) + res_model = res_models.pop() + request.env[res_model].check_access_rights("read") request.env[res_model].browse(res_ids).check_access_rule("read") + + followers = [] + follower_id = None for follower in follower_recs: - is_uid = partner_id == follower.partner_id - follower_id = follower.id if is_uid else follower_id + if follower.partner_id == request.env.user.partner_id: + follower_id = follower.id followers.append({ 'id': follower.id, - 'name': follower.partner_id.name or follower.channel_id.name, - 'email': follower.partner_id.email if follower.partner_id else None, - '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': is_uid, - 'active': follower.partner_id.active or bool(follower.channel_id), + 'partner_id': follower.partner_id.id, + 'channel_id': follower.channel_id.id, + 'name': follower.name, + 'email': follower.email, + 'is_active': follower.is_active, + # When editing the followers, the "pencil" icon that leads to the edition of subtypes + # should be always be displayed and not only when "debug" mode is activated. + 'is_editable': True }) return { 'followers': followers, - 'subtypes': self.read_subscription_data(res_model, follower_id) if follower_id else None + 'subtypes': self.read_subscription_data(follower_id) if follower_id else None } @http.route('/mail/read_subscription_data', type='json', auth='user') - def read_subscription_data(self, res_model, follower_id): + def read_subscription_data(self, follower_id): """ Computes: - message_subtype_data: data about document subtypes: which are available, which are followed if any """ - followers = request.env['mail.followers'].browse(follower_id) + request.env['mail.followers'].check_access_rights("read") + follower = request.env['mail.followers'].sudo().browse(follower_id) + follower.ensure_one() + request.env[follower.res_model].check_access_rights("read") + request.env[follower.res_model].browse(follower.res_id).check_access_rule("read") # find current model subtypes, add them to a dictionary - subtypes = request.env['mail.message.subtype'].search(['&', ('hidden', '=', False), '|', ('res_model', '=', res_model), ('res_model', '=', False)]) + subtypes = request.env['mail.message.subtype'].search([ + '&', ('hidden', '=', False), + '|', ('res_model', '=', follower.res_model), ('res_model', '=', False)]) + followed_subtypes_ids = set(follower.subtype_ids.ids) subtypes_list = [{ 'name': subtype.name, 'res_model': subtype.res_model, 'sequence': subtype.sequence, 'default': subtype.default, 'internal': subtype.internal, - 'followed': subtype.id in followers.mapped('subtype_ids').ids, + 'followed': subtype.id in followed_subtypes_ids, 'parent_model': subtype.parent_id.res_model, 'id': subtype.id } for subtype in subtypes] - subtypes_list = sorted(subtypes_list, key=lambda it: (it['parent_model'] or '', it['res_model'] or '', it['internal'], it['sequence'])) - return subtypes_list + return sorted(subtypes_list, + key=lambda it: (it['parent_model'] or '', it['res_model'] or '', it['internal'], it['sequence'])) @http.route('/mail/view', type='http', auth='public') def mail_action_view(self, model=None, res_id=None, access_token=None, **kwargs): diff --git a/addons/mail/models/mail_followers.py b/addons/mail/models/mail_followers.py index b9b3f53e8b940abc19309a1eb0543ab5b84dc4c6..2cd2d48b38d1979362109e9b5d6ccc4d27c2ac94 100644 --- a/addons/mail/models/mail_followers.py +++ b/addons/mail/models/mail_followers.py @@ -35,6 +35,12 @@ class Followers(models.Model): subtype_ids = fields.Many2many( 'mail.message.subtype', string='Subtype', help="Message subtypes followed, meaning subtypes that will be pushed onto the user's Wall.") + name = fields.Char('Name', compute='_compute_related_fields', + help="Name of the related partner (if exist) or the related channel") + email = fields.Char('Email', compute='_compute_related_fields', + help="Email of the related partner (if exist) or False") + is_active = fields.Boolean('Is Active', compute='_compute_related_fields', + help="If the related partner is active (if exist) or if related channel exist") def _invalidate_documents(self, vals_list=None): """ Invalidate the cache of the documents followed by ``self``. @@ -75,6 +81,18 @@ class Followers(models.Model): # Private tools methods to fetch followers data # -------------------------------------------------- + @api.depends('partner_id', 'channel_id') + def _compute_related_fields(self): + for follower in self: + if follower.partner_id: + follower.name = follower.partner_id.name + follower.email = follower.partner_id.email + follower.is_active = follower.partner_id.active + else: + follower.name = follower.channel_id.name + follower.is_active = bool(follower.channel_id) + follower.email = False + def _get_recipient_data(self, records, message_type, subtype_id, pids=None, cids=None): """ Private method allowing to fetch recipients data based on a subtype. Purpose of this method is to fetch all data necessary to notify recipients diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 4672cd4d569712782726ac0a0a9678564f897b57..c83d4ae8705d3b357aaca2d68648f61409dc7c79 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -82,13 +82,15 @@ class MailThread(models.AbstractModel): message_is_follower = fields.Boolean( 'Is Follower', compute='_compute_is_follower', search='_search_is_follower') message_follower_ids = fields.One2many( - 'mail.followers', 'res_id', string='Followers') + 'mail.followers', 'res_id', string='Followers', groups='base.group_user') message_partner_ids = fields.Many2many( comodel_name='res.partner', string='Followers (Partners)', - compute='_get_followers', search='_search_follower_partners') + compute='_get_followers', search='_search_follower_partners', + groups='base.group_user') message_channel_ids = fields.Many2many( comodel_name='mail.channel', string='Followers (Channels)', - compute='_get_followers', search='_search_follower_channels') + compute='_get_followers', search='_search_follower_channels', + groups='base.group_user') message_ids = fields.One2many( 'mail.message', 'res_id', string='Messages', domain=lambda self: [('message_type', '!=', 'user_notification')], auto_join=True) @@ -258,17 +260,16 @@ class MailThread(models.AbstractModel): if self._context.get('tracking_disable'): return super(MailThread, self).create(vals_list) + threads = super(MailThread, self).create(vals_list) # subscribe uid unless asked not to if not self._context.get('mail_create_nosubscribe'): - default_followers = self.env['mail.followers']._add_default_followers( - self._name, [], self.env.user.partner_id.ids, customer_ids=[], - check_existing=False, existing_policy='skip')[0][0] - for values in vals_list: - message_follower_ids = values.get('message_follower_ids') or [] - message_follower_ids += [(0, 0, fol_vals) for fol_vals in default_followers] - values['message_follower_ids'] = message_follower_ids - - threads = super(MailThread, self).create(vals_list) + for thread in threads: + self.env['mail.followers']._insert_followers( + thread._name, thread.ids, self.env.user.partner_id.ids, + None, None, None, + customer_ids=[], + check_existing=False + ) # auto_subscribe: take values and defaults into account create_values_list = {} diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv index eb52637ff2e2c5b56406cffde3006515eefd61a1..2f21338e9f4f8b41f1fb7d634fdd7e590ad52d9e 100644 --- a/addons/mail/security/ir.model.access.csv +++ b/addons/mail/security/ir.model.access.csv @@ -6,9 +6,8 @@ access_mail_mail_all,mail.mail.all,model_mail_mail,,0,0,0,0 access_mail_mail_portal,mail.mail.portal,model_mail_mail,base.group_portal,0,0,0,0 access_mail_mail_user,mail.mail.user,model_mail_mail,base.group_user,0,0,0,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,1,0 -access_mail_followers_user,mail.followers.user,model_mail_followers,base.group_user,1,1,1,0 +access_mail_followers_all,mail.followers.all,model_mail_followers,,0,0,0,0 +access_mail_followers_user,mail.followers.user,model_mail_followers,base.group_user,1,0,0,0 access_mail_followers_system,mail.followers.system,model_mail_followers,base.group_system,1,1,1,1 access_mail_notification_portal,mail.notification.portal,model_mail_notification,base.group_portal,1,0,0,0 access_mail_notification_user,mail.notification.user,model_mail_notification,base.group_user,1,1,1,0 diff --git a/addons/mail/security/mail_security.xml b/addons/mail/security/mail_security.xml index 3ffe43e35eb4513eeba3e2b359cba5c05b74aa4d..3a98e1bddb2b78629f01a5c058b2991c21433eeb 100644 --- a/addons/mail/security/mail_security.xml +++ b/addons/mail/security/mail_security.xml @@ -14,16 +14,6 @@ </record> </data> <data noupdate="1"> - <record id="mail_followers_read_write_own" model="ir.rule"> - <field name="name">mail.followers: write its own entries</field> - <field name="model_id" ref="model_mail_followers"/> - <field name="groups" eval="[(4, ref('base.group_user')), (4, ref('base.group_portal'))]"/> - <field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field> - <field name="perm_create" eval="False"/> - <field name="perm_unlink" eval="False"/> - <field name="perm_read" eval="False"/> - </record> - <record id="ir_rule_mail_notifications_group_user" model="ir.rule"> <field name="name">mail.notifications: group_user: write its own entries</field> <field name="model_id" ref="model_mail_notification"/> diff --git a/addons/mail/static/src/js/followers.js b/addons/mail/static/src/js/followers.js index cbe20ba240e9623134fa121a12756a6cc9b177c2..7179b159663d7897a8d0e5d47d644eb26e901630 100644 --- a/addons/mail/static/src/js/followers.js +++ b/addons/mail/static/src/js/followers.js @@ -6,6 +6,7 @@ var concurrency = require('web.concurrency'); var core = require('web.core'); var Dialog = require('web.Dialog'); var field_registry = require('web.field_registry'); +var session = require('web.session'); var _t = core._t; var QWeb = core.qweb; @@ -43,7 +44,6 @@ var Followers = AbstractField.extend({ this.subtypes = []; this.data_subtype = {}; this._isFollower = undefined; - var session = this.getSession(); this.partnerID = session.partner_id; this.dp = new concurrency.DropPrevious(); @@ -113,14 +113,14 @@ var Followers = AbstractField.extend({ $(QWeb.render('mail.Followers.add_more', {widget: this})).appendTo($followers_list); var $follower_li; _.each(this.followers, function (record) { - if(!record.active) { + if (!record.is_active) { record.title = _.str.sprintf(_t('%s \n(inactive)'), record.name); } else { record.title = record.name; } $follower_li = $(QWeb.render('mail.Followers.partner', { - 'record': _.extend(record, {'avatar_url': '/web/image/' + record.res_model + '/' + record.res_id + '/image_128'}), + 'record': record, 'widget': self}) ); $follower_li.appendTo($followers_list); @@ -199,28 +199,37 @@ var Followers = AbstractField.extend({ return str; }, _readFollowers: function () { - var self = this; var missing_ids = _.difference(this.value.res_ids, _.pluck(this.followers, 'id')); var def; if (missing_ids.length) { def = this._rpc({ route: '/mail/read_followers', - params: { follower_ids: missing_ids, res_model: this.model, context: {} } // empty context to be overridden in session.js with 'allowed_company_ids' + params: { follower_ids: missing_ids, context: {} } // empty context to be overridden in session.js with 'allowed_company_ids' }); } - return Promise.resolve(def).then(function (results) { + return Promise.resolve(def).then((results) => { if (results) { - self.followers = _.uniq(results.followers.concat(self.followers), 'id'); + // Preprocess records + _.each(results.followers, (record) => { + var resModel = record.partner_id ? 'res.partner' : 'mail.channel'; + var resId = record.partner_id ? record.partner_id : record.channel_id; + record.res_id = resId; + record.res_model = resModel; + record.avatar_url = '/web/image/' + resModel + '/' + resId + '/image_128'; + }); + this.followers = _.uniq(results.followers.concat(this.followers), 'id'); if (results.subtypes) { //read_followers will return False if current user is not in the list - self.subtypes = results.subtypes; + this.subtypes = results.subtypes; } } // filter out previously fetched followers that are no longer following - self.followers = _.filter(self.followers, function (follower) { - return _.contains(self.value.res_ids, follower.id); + this.followers = _.filter(this.followers, (follower) => { + return _.contains(this.value.res_ids, follower.id); + }); + var userFollower = _.filter(this.followers, (rec) => { + return this.partnerID === rec.partner_id; }); - var user_follower = _.filter(self.followers, function (rec) { return rec.is_uid; }); - self._isFollower = user_follower.length >= 1; + this._isFollower = userFollower.length >= 1; }); }, _reload: function () { @@ -361,7 +370,7 @@ var Followers = AbstractField.extend({ var follower_id = $currentTarget.data('follower-id'); // id of model mail_follower this._rpc({ route: '/mail/read_subscription_data', - params: {res_model: this.model, follower_id: follower_id}, + params: {follower_id: follower_id}, }) .then(function (data) { var res_id = $currentTarget.data('oe-id'); // id of model res_partner or mail_channel diff --git a/addons/mail/static/src/xml/followers.xml b/addons/mail/static/src/xml/followers.xml index 8a0914737a2c764f1c1a4fc63713a3df1fe75598..8789b424af2e28aa199e4d9d27eaac2b41a4c38d 100644 --- a/addons/mail/static/src/xml/followers.xml +++ b/addons/mail/static/src/xml/followers.xml @@ -30,12 +30,12 @@ Partner or channel following the record --> <t t-name="mail.Followers.partner"> - <div role="menuitem" t-attf-class="dropdown-item o_partner {{ !record.active ? 'o_inactive': '' }}"> + <div role="menuitem" t-attf-class="dropdown-item o_partner {{ !record.is_active ? 'o_inactive': '' }}"> <a class="o_mail_redirect text-truncate" href="#" t-att-title="record.title" - t-att-data-oe-model="record.res_model" - t-att-data-oe-id="record.res_id"> + t-att-data-oe-model="record.partner_id ? 'res.partner' : 'mail.channel'" + t-att-data-oe-id="record.partner_id ? record.partner_id : record.channel_id"> <img t-att-src="record.avatar_url" alt="Avatar" class="o_image_64_cover"/> <t t-esc="record.name"/> </a> @@ -45,8 +45,8 @@ aria-label="Edit subscription" role="img" t-att-data-follower-id="record.id" - t-att-data-oe-id="record.res_id" - t-att-data-oe-model="record.res_model"/> + t-att-data-oe-id="record.partner_id ? record.partner_id : record.channel_id" + t-att-data-oe-model="record.partner_id ? 'res.partner' : 'mail.channel'"/> <button t-if="widget.isEditable" class="btn btn-icon fa fa-remove o_remove_follower" aria-label="Remove this follower" diff --git a/addons/mail/static/tests/chatter_tests.js b/addons/mail/static/tests/chatter_tests.js index bc1ff44b746d5b23752f5ff84b7227a9fc6a5bf2..f26dd096e62ad0a2aa8b3095dcce327a6267d7ec 100644 --- a/addons/mail/static/tests/chatter_tests.js +++ b/addons/mail/static/tests/chatter_tests.js @@ -2490,11 +2490,11 @@ QUnit.test('followers widget: follow/unfollow, edit subtypes', async function (a this.data.partner.records[0].message_follower_ids = [1]; followers.push({ id: 1, - is_uid: true, name: "Admin", email: "admin@example.com", - res_id: resID, - res_model: 'partner', + partner_id: partnerID, + channel_id: null, + is_active: true }); } return Promise.resolve(true); @@ -2558,18 +2558,18 @@ QUnit.test('followers widget: follow/unfollow confirmation dialog', async functi var partnerID = 2; var followers = [{ id: 1, - is_uid: true, name: "Admin", email: "admin@example.com", - res_id: resID, - res_model: 'res.partner', + partner_id: partnerID, + channel_id: null, + is_active: true }, { id: 2, - is_uid: true, name: "Demo", email: "demo@example.com", - res_id: 3, - res_model: 'res.partner', + partner_id: 1, + channel_id: null, + is_active: true }]; var form = await createView({ @@ -2624,12 +2624,14 @@ QUnit.test('followers widget: do not display follower duplications', async funct this.data.partner.records[0].message_follower_ids = [1]; var resID = 2; + var partnerID = 1; var followers = [{ id: 1, name: "Admin", email: "admin@example.com", - res_id: resID, - res_model: 'partner', + partner_id: partnerID, + channel_id: null, + is_active: true }]; var def; var form = await createView({ @@ -2657,17 +2659,18 @@ QUnit.test('followers widget: do not display follower duplications', async funct return this._super.apply(this, arguments); }, res_id: resID, - session: {partner_id: 1}, + session: {partner_id: partnerID}, }); followers.push({ id: 2, - is_uid: false, name: "A follower", email: "follower@example.com", res_id: resID, - res_model: 'partner', + partner_id: 555, + channel_id: null, + is_active: true }); this.data.partner.records[0].message_follower_ids.push(2); @@ -2695,23 +2698,23 @@ QUnit.test('followers widget: display inactive followers with a different style' id: 1, name: "Admin", email: "admin@example.com", - res_id: 101, - res_model: 'partner', - active: true, + partner_id: 101, + channel_id: false, + is_active: true, },{ id: 2, name: "Active_partner", email: "active_partner@example.com", - res_id: 102, - res_model: 'partner', - active: true, + partner_id: 102, + channel_id: false, + is_active: true, },{ id: 3, name: "Inactive_partner", email: "inactive_partner@example.com", - res_id: 103, - res_model: 'partner', - active: false, + partner_id: 103, + channel_id: false, + is_active: false, }]; var form = await createView({ @@ -3002,11 +3005,9 @@ QUnit.test('chatter: suggested partner auto-follow on message post', async funct var followers = []; followers.push({ id: 1, - is_uid: true, name: "Admin", email: "admin@example.com", - res_id: 5, - res_model: 'partner', + partner_id: 5, }); var form = await createView({ @@ -3045,11 +3046,11 @@ QUnit.test('chatter: suggested partner auto-follow on message post', async funct self.data.partner.records[0].message_follower_ids.push(2); followers.push({ id: 2, - is_uid: true, name: "Demo User", email: "demo-user@example.com", - res_id: 8, - res_model: 'partner', + partner_id: 8, + channel_id: false, + is_active: true }); // post a legit message so that it does not crashes @@ -3187,14 +3188,16 @@ QUnit.test('chatter: mention prefetched partners (followers & employees)', async id: 10, name: 'FollowerUser1', email: 'follower-user1@example.com', - res_model: 'res.partner', - res_id: 1, + partner_id: 1, + channel_id: null, + is_active: true }, { id: 20, name: 'FollowerUser2', email: 'follower-user2@example.com', - res_model: 'res.partner', - res_id: 2, + partner_id: 2, + channel_id: null, + is_active: true, }], subtypes: [], }); diff --git a/addons/test_mail/tests/test_mail_channel.py b/addons/test_mail/tests/test_mail_channel.py index 8869ac214422885b70bdf684ec32ccc682e2cd7a..2e3c7c2cb4b85d395beabc5be206b4cc1a6979f5 100644 --- a/addons/test_mail/tests/test_mail_channel.py +++ b/addons/test_mail/tests/test_mail_channel.py @@ -99,13 +99,16 @@ class TestChannelAccessRights(TestMailCommon): trigger_read = chell_pigs.name for message in chell_pigs.message_ids: trigger_read = message.subject - for partner in chell_pigs.message_partner_ids: + + with self.assertRaises(AccessError): + chell_pigs.message_partner_ids + + for partner in self.group_private.message_partner_ids: if partner.id == self.user_portal.partner_id.id: # Chell can read her own partner record continue - # TODO Change the except_orm to Warning - with self.assertRaises(except_orm): - trigger_read = partner.name + with self.assertRaises(AccessError): + trigger_read = partner.with_user(self.user_portal).name class TestChannelFeatures(TestMailCommon): diff --git a/addons/website_mail/controllers/main.py b/addons/website_mail/controllers/main.py index 6d00c06414972ab7ae580c5df72f8884467f3f4b..822373eb4d6eea95ba122ef00c14ecc9397f77b7 100644 --- a/addons/website_mail/controllers/main.py +++ b/addons/website_mail/controllers/main.py @@ -11,7 +11,12 @@ class WebsiteMail(http.Controller): # TDE FIXME: check this method with new followers res_id = int(id) is_follower = message_is_follower == 'on' - record = request.env[object].browse(res_id) + record = request.env[object].browse(res_id).exists() + if not record: + return False + + record.check_access_rights('read') + record.check_access_rule('read') # search partner_id if request.env.user != request.website.user_id: @@ -24,11 +29,9 @@ class WebsiteMail(http.Controller): partner_ids = request.env['res.partner'].sudo().create({'name': name, 'email': email}).ids # add or remove follower if is_follower: - record.check_access_rule('read') record.sudo().message_unsubscribe(partner_ids) return False else: - record.check_access_rule('read') # add partner to session request.session['partner_id'] = partner_ids[0] record.sudo().message_subscribe(partner_ids) @@ -48,14 +51,13 @@ class WebsiteMail(http.Controller): 'is_user': user != public_user, 'email': partner.email if partner else "", 'is_follower': False, - 'alias_name': False, } - record = request.env[model].sudo().browse(int(res_id)) - if record and partner: - values['is_follower'] = bool(request.env['mail.followers'].search_count([ + record_sudo = request.env[model].sudo().browse(int(res_id)) + if partner and record_sudo.exists(): + values['is_follower'] = bool(request.env['mail.followers'].sudo().search_count([ ('res_model', '=', model), - ('res_id', '=', record.id), + ('res_id', '=', record_sudo.id), ('partner_id', '=', partner.id) ])) return values