[FIX] mail, im_livechat, *: restore usage of livechat user name
* = website_livechat Prevents from leaking unnecessary information to livechat clients. opw-2453956 closes odoo/odoo#82026 X-original-commit: 5ec191d288d5b44f78cd1f6980c47bb64cfd9bd1 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com> Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -32,7 +32,7 @@
|
||||
<td style="padding: 0 50px;">
|
||||
<div style="font-size: 13px; padding: 10px 0;">
|
||||
<span>Hello,</span><br />Here's a copy of your conversation with
|
||||
<span t-field="channel.livechat_operator_id.name"/>, on the
|
||||
<span t-esc="channel.livechat_operator_id.user_livechat_username or channel.livechat_operator_id.name"/>, on the
|
||||
<span t-field="channel.livechat_channel_id.create_date"/>
|
||||
</div>
|
||||
<table cellspacing="0" cellpadding="0" style="width:100%; border-collapse: collapse;">
|
||||
|
||||
@@ -5,6 +5,7 @@ from . import res_partner
|
||||
from . import im_livechat_channel
|
||||
from . import mail_channel
|
||||
from . import mail_channel_partner
|
||||
from . import mail_message
|
||||
from . import res_users_settings
|
||||
from . import rating
|
||||
from . import digest
|
||||
|
||||
@@ -35,10 +35,7 @@ class MailChannel(models.Model):
|
||||
clicking on livechat button). So when the anonymous person is sending its FIRST message, the channel header
|
||||
should be added to the notification, since the user cannot be listining to the channel.
|
||||
"""
|
||||
livechat_channels = self.filtered(lambda x: x.channel_type == 'livechat')
|
||||
other_channels = self.filtered(lambda x: x.channel_type != 'livechat')
|
||||
notifications = super(MailChannel, livechat_channels)._channel_message_notifications(message.with_context(im_livechat_use_username=True)) + \
|
||||
super(MailChannel, other_channels)._channel_message_notifications(message, message_format)
|
||||
notifications = super()._channel_message_notifications(message=message, message_format=message_format)
|
||||
for channel in self:
|
||||
# add uuid for private livechat channels to allow anonymous to listen
|
||||
if channel.channel_type == 'livechat' and channel.public == 'private':
|
||||
@@ -50,11 +47,6 @@ class MailChannel(models.Model):
|
||||
notifications = self._channel_channel_notifications(unpinned_channel_partner.mapped('partner_id').ids) + notifications
|
||||
return notifications
|
||||
|
||||
def _channel_fetch_message(self, last_id=False, limit=20):
|
||||
""" Override to add the context of the livechat username."""
|
||||
channel = self.with_context(im_livechat_use_username=True) if self.channel_type == 'livechat' else self
|
||||
return super(MailChannel, channel)._channel_fetch_message(last_id=last_id, limit=limit)
|
||||
|
||||
def channel_info(self):
|
||||
""" Extends the channel header by adding the livechat operator and the 'anonymous' profile
|
||||
:rtype : list(dict)
|
||||
@@ -66,12 +58,33 @@ class MailChannel(models.Model):
|
||||
if channel.channel_type == 'livechat':
|
||||
# add the operator id
|
||||
if channel.livechat_operator_id:
|
||||
res = channel.livechat_operator_id.with_context(im_livechat_use_username=True).name_get()[0]
|
||||
channel_infos_dict[channel.id]['operator_pid'] = (res[0], res[1].replace(',', ''))
|
||||
display_name = channel.livechat_operator_id.user_livechat_username or channel.livechat_operator_id.display_name
|
||||
channel_infos_dict[channel.id]['operator_pid'] = (channel.livechat_operator_id.id, display_name.replace(',', ''))
|
||||
# add the anonymous or partner name
|
||||
channel_infos_dict[channel.id]['livechat_visitor'] = channel._channel_get_livechat_visitor_info()
|
||||
return list(channel_infos_dict.values())
|
||||
|
||||
def _channel_info_format_member(self, partner, partner_info):
|
||||
"""Override to remove sensitive information in livechat."""
|
||||
if self.channel_type == 'livechat':
|
||||
return {
|
||||
'active': partner.active,
|
||||
'id': partner.id,
|
||||
'name': partner.user_livechat_username or partner.name, # for API compatibility in stable
|
||||
'email': False, # for API compatibility in stable
|
||||
'im_status': False, # for API compatibility in stable
|
||||
'livechat_username': partner.user_livechat_username,
|
||||
}
|
||||
return super()._channel_info_format_member(partner=partner, partner_info=partner_info)
|
||||
|
||||
def _notify_typing_partner_data(self):
|
||||
"""Override to remove name and return livechat username if applicable."""
|
||||
data = super()._notify_typing_partner_data()
|
||||
if self.channel_type == 'livechat' and self.env.user.partner_id.user_livechat_username:
|
||||
data['partner_name'] = self.env.user.partner_id.user_livechat_username # for API compatibility in stable
|
||||
data['livechat_username'] = self.env.user.partner_id.user_livechat_username
|
||||
return data
|
||||
|
||||
def _channel_get_livechat_visitor_info(self):
|
||||
self.ensure_one()
|
||||
# remove active test to ensure public partner is taken into account
|
||||
@@ -171,7 +184,7 @@ class MailChannel(models.Model):
|
||||
mail_body = template._render(render_context, engine='ir.qweb', minimal_qcontext=True)
|
||||
mail_body = self.env['mail.render.mixin']._replace_local_links(mail_body)
|
||||
mail = self.env['mail.mail'].sudo().create({
|
||||
'subject': _('Conversation with %s', self.livechat_operator_id.name),
|
||||
'subject': _('Conversation with %s', self.livechat_operator_id.user_livechat_username or self.livechat_operator_id.name),
|
||||
'email_from': company.catchall_formatted or company.email_formatted,
|
||||
'author_id': self.env.user.partner_id.id,
|
||||
'email_to': email,
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class MailMessage(models.Model):
|
||||
_inherit = 'mail.message'
|
||||
|
||||
def _message_format(self, fnames, format_reply=True):
|
||||
"""Override to remove email_from and to return the livechat username if applicable.
|
||||
A third param is added to the author_id tuple in this case to be able to differentiate it
|
||||
from the normal name in client code."""
|
||||
vals_list = super()._message_format(fnames=fnames, format_reply=format_reply)
|
||||
for vals in vals_list:
|
||||
message_sudo = self.browse(vals['id']).sudo().with_prefetch(self.ids)
|
||||
if message_sudo.model == 'mail.channel' and self.env['mail.channel'].browse(message_sudo.res_id).channel_type == 'livechat':
|
||||
vals.pop('email_from')
|
||||
if message_sudo.author_id.user_livechat_username:
|
||||
vals['author_id'] = (message_sudo.author_id.id, message_sudo.author_id.user_livechat_username, message_sudo.author_id.user_livechat_username)
|
||||
return vals_list
|
||||
@@ -1,38 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
from odoo import api, models, fields
|
||||
|
||||
|
||||
class Partners(models.Model):
|
||||
""" Update of res.partners class
|
||||
- override name_get to take into account the livechat username
|
||||
"""
|
||||
"""Update of res.partner class to take into account the livechat username."""
|
||||
_inherit = 'res.partner'
|
||||
|
||||
def name_get(self):
|
||||
if self.env.context.get('im_livechat_use_username'):
|
||||
# process the ones with livechat username
|
||||
users_with_livechatname = self.env['res.users'].search([('partner_id', 'in', self.ids), ('livechat_username', '!=', False)])
|
||||
map_with_livechatname = {}
|
||||
for user in users_with_livechatname:
|
||||
map_with_livechatname[user.partner_id.id] = user.livechat_username
|
||||
|
||||
# process the ones without livecaht username
|
||||
partner_without_livechatname = self - users_with_livechatname.mapped('partner_id')
|
||||
no_livechatname_name_get = super(Partners, partner_without_livechatname).name_get()
|
||||
map_without_livechatname = dict(no_livechatname_name_get)
|
||||
|
||||
# restore order
|
||||
result = []
|
||||
for partner in self:
|
||||
name = map_with_livechatname.get(partner.id)
|
||||
if not name:
|
||||
name = map_without_livechatname.get(partner.id)
|
||||
result.append((partner.id, name))
|
||||
else:
|
||||
result = super(Partners, self).name_get()
|
||||
return result
|
||||
user_livechat_username = fields.Char(compute='_compute_user_livechat_username')
|
||||
|
||||
def _get_channels_as_member(self):
|
||||
channels = super()._get_channels_as_member()
|
||||
@@ -44,3 +20,8 @@ class Partners(models.Model):
|
||||
])),
|
||||
])
|
||||
return channels
|
||||
|
||||
@api.depends('user_ids.livechat_username')
|
||||
def _compute_user_livechat_username(self):
|
||||
for partner in self:
|
||||
partner.user_livechat_username = next(iter(partner.user_ids.mapped('livechat_username')), False)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { patchModelMethods } from '@mail/model/model_core';
|
||||
import { insertAndReplace } from '@mail/model/model_field_command';
|
||||
// ensure the model definition is loaded before the patch
|
||||
import '@mail/models/message/message';
|
||||
|
||||
patchModelMethods('Message', {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
convertData(data) {
|
||||
const data2 = this._super(data);
|
||||
if ('author_id' in data) {
|
||||
if (data.author_id[2]) {
|
||||
// flux specific for livechat, a 3rd param is livechat_username
|
||||
// and means 2nd param (display_name) should be ignored
|
||||
data2.author = insertAndReplace({
|
||||
id: data.author_id[0],
|
||||
livechat_username: data.author_id[2],
|
||||
});
|
||||
}
|
||||
}
|
||||
return data2;
|
||||
},
|
||||
});
|
||||
+12
-3
@@ -21,7 +21,7 @@ patchRecordMethods('MessagingNotificationHandler', {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_handleNotificationChannelPartnerTypingStatus({ channel_id, is_typing, partner_id, partner_name }) {
|
||||
_handleNotificationChannelPartnerTypingStatus({ channel_id, is_typing, livechat_username, partner_id, partner_name }) {
|
||||
const channel = this.messaging.models['Thread'].findFromIdentifyingData({
|
||||
id: channel_id,
|
||||
model: 'mail.channel',
|
||||
@@ -40,11 +40,20 @@ patchRecordMethods('MessagingNotificationHandler', {
|
||||
partnerId = partner_id;
|
||||
partnerName = partner_name;
|
||||
}
|
||||
this._super({
|
||||
const data = {
|
||||
channel_id,
|
||||
is_typing,
|
||||
partner_id: partnerId,
|
||||
partner_name: partnerName,
|
||||
});
|
||||
};
|
||||
if (livechat_username) {
|
||||
// flux specific, livechat_username is returned instead of name for livechat channels
|
||||
delete data.partner_name; // value still present for API compatibility in stable
|
||||
this.models['Partner'].insert({
|
||||
id: partnerId,
|
||||
livechat_username: livechat_username,
|
||||
});
|
||||
}
|
||||
this._super(data);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { addModelMethods } from '@mail/model/model_core';
|
||||
import { addFields, addModelMethods, patchModelMethods } from '@mail/model/model_core';
|
||||
import { attr } from '@mail/model/model_field';
|
||||
|
||||
// ensure that the model definition is loaded before the patch
|
||||
import '@mail/models/partner/partner';
|
||||
|
||||
@@ -14,3 +16,27 @@ addModelMethods('Partner', {
|
||||
},
|
||||
});
|
||||
|
||||
patchModelMethods('Partner', {
|
||||
convertData(data) {
|
||||
const data2 = this._super(data);
|
||||
if ('livechat_username' in data) {
|
||||
// flux specific, if livechat username is present it means `name`,
|
||||
// `email` and `im_status` contain `false` even though their value
|
||||
// might actually exist. Remove them from data2 to avoid overwriting
|
||||
// existing value (that could be known through other means).
|
||||
delete data2.name;
|
||||
delete data2.email;
|
||||
delete data2.im_status;
|
||||
data2.livechat_username = data.livechat_username;
|
||||
}
|
||||
return data2;
|
||||
},
|
||||
});
|
||||
|
||||
addFields('Partner', {
|
||||
/**
|
||||
* States the specific name of this partner in the context of livechat.
|
||||
* Either a string or undefined.
|
||||
*/
|
||||
livechat_username: attr(),
|
||||
});
|
||||
|
||||
@@ -77,6 +77,15 @@ patchModelMethods('Thread', {
|
||||
});
|
||||
|
||||
patchRecordMethods('Thread', {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
getMemberName(partner) {
|
||||
if (this.channel_type === 'livechat' && partner.livechat_username) {
|
||||
return partner.livechat_username;
|
||||
}
|
||||
return this._super(partner);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,8 @@ class TestGetMailChannel(TransactionCase):
|
||||
super(TestGetMailChannel, self).setUp()
|
||||
self.operators = self.env['res.users'].create([{
|
||||
'name': 'Michel',
|
||||
'login': 'michel'
|
||||
'login': 'michel',
|
||||
'livechat_username': "Michel Operator",
|
||||
}, {
|
||||
'name': 'Paul',
|
||||
'login': 'paul'
|
||||
@@ -58,12 +59,30 @@ class TestGetMailChannel(TransactionCase):
|
||||
test_user = self.env['res.users'].create({'name': 'Roger', 'login': 'roger', 'country_id': belgium.id})
|
||||
|
||||
# ensure visitor info are correct with anonymous
|
||||
channel_info = self.livechat_channel.with_user(public_user)._open_livechat_mail_channel(anonymous_name='Visitor 22', country_id=belgium.id)
|
||||
operator = self.operators[0]
|
||||
channel_info = self.livechat_channel.with_user(public_user)._open_livechat_mail_channel(anonymous_name='Visitor 22', previous_operator_id=operator.partner_id.id, country_id=belgium.id)
|
||||
visitor_info = channel_info['livechat_visitor']
|
||||
self.assertFalse(visitor_info['id'])
|
||||
self.assertEqual(visitor_info['name'], "Visitor 22")
|
||||
self.assertEqual(visitor_info['country'], (20, "Belgium"))
|
||||
|
||||
# ensure member info are hidden (in particular email and real name when livechat username is present)
|
||||
self.assertEqual(sorted(channel_info['members'], key=lambda m: m['id']), sorted([{
|
||||
'active': True,
|
||||
'email': False,
|
||||
'id': operator.partner_id.id,
|
||||
'im_status': False,
|
||||
'livechat_username': 'Michel Operator',
|
||||
'name': 'Michel Operator',
|
||||
}, {
|
||||
'active': False,
|
||||
'email': False,
|
||||
'id': public_user.partner_id.id,
|
||||
'im_status': False,
|
||||
'livechat_username': False,
|
||||
'name': 'Public user',
|
||||
}], key=lambda m: m['id']))
|
||||
|
||||
# ensure visitor info are correct with real user
|
||||
channel_info = self.livechat_channel.with_user(test_user)._open_livechat_mail_channel(anonymous_name='whatever', user_id=test_user.id)
|
||||
visitor_info = channel_info['livechat_visitor']
|
||||
@@ -74,7 +93,7 @@ class TestGetMailChannel(TransactionCase):
|
||||
# ensure visitor info are correct when operator is testing himself
|
||||
operator = self.operators[0]
|
||||
channel_info = self.livechat_channel.with_user(operator)._open_livechat_mail_channel(anonymous_name='whatever', previous_operator_id=operator.partner_id.id, user_id=operator.id)
|
||||
self.assertEqual(channel_info['operator_pid'], (operator.partner_id.id, "Michel"))
|
||||
self.assertEqual(channel_info['operator_pid'], (operator.partner_id.id, "Michel Operator"))
|
||||
visitor_info = channel_info['livechat_visitor']
|
||||
self.assertEqual(visitor_info['id'], operator.partner_id.id)
|
||||
self.assertEqual(visitor_info['name'], "Michel")
|
||||
@@ -100,3 +119,22 @@ class TestGetMailChannel(TransactionCase):
|
||||
self.env['mail.channel'].browse(channel_info['id']).message_post(body='cc')
|
||||
self.assertTrue(operator_channel_partner.is_pinned, "channel should be pinned for operator after visitor sent a message")
|
||||
self.assertIn(channel_info['id'], operator_channel_partner.partner_id._get_channels_as_member().ids, "channel should be fetched by operator on new page")
|
||||
|
||||
def test_operator_livechat_username(self):
|
||||
"""Ensures the operator livechat_username is returned by `_channel_fetch_message`, which is
|
||||
the method called by the public route displaying chat history."""
|
||||
public_user = self.env.ref('base.public_user')
|
||||
operator = self.operators[0]
|
||||
operator.write({
|
||||
'email': 'michel@example.com',
|
||||
'livechat_username': 'Michel at your service',
|
||||
})
|
||||
channel_info = self.livechat_channel.with_user(public_user)._open_livechat_mail_channel(anonymous_name='whatever')
|
||||
channel = self.env['mail.channel'].browse(channel_info['id'])
|
||||
channel.with_user(operator).message_post(body='Hello', message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
message_formats = channel.with_user(public_user)._channel_fetch_message()
|
||||
self.assertEqual(len(message_formats), 1)
|
||||
self.assertEqual(message_formats[0]['author_id'][0], operator.partner_id.id)
|
||||
self.assertEqual(message_formats[0]['author_id'][1], operator.livechat_username)
|
||||
self.assertEqual(message_formats[0]['author_id'][2], operator.livechat_username)
|
||||
self.assertFalse(message_formats[0].get('email_from'), "should not send email_from to livechat user")
|
||||
|
||||
@@ -771,7 +771,7 @@ class Channel(models.Model):
|
||||
# avoid sending potentially a lot of members for big channels
|
||||
# exclude chat and other small channels from this optimization because they are
|
||||
# assumed to be smaller and it's important to know the member list for them
|
||||
info['members'] = sorted(list(partner_format_by_partner[member.partner_id] for member in members_by_channel[channel] if member.partner_id), key=lambda p: p['id'])
|
||||
info['members'] = sorted(list(channel._channel_info_format_member(member.partner_id, partner_format_by_partner[member.partner_id]) for member in members_by_channel[channel] if member.partner_id), key=lambda p: p['id'])
|
||||
info['seen_partners_info'] = sorted([{
|
||||
'id': cp.id,
|
||||
'partner_id': cp.partner_id.id,
|
||||
@@ -789,6 +789,11 @@ class Channel(models.Model):
|
||||
channel_infos.append(info)
|
||||
return channel_infos
|
||||
|
||||
def _channel_info_format_member(self, partner, partner_info):
|
||||
"""Returns member information in the context of self channel."""
|
||||
self.ensure_one()
|
||||
return partner_info
|
||||
|
||||
def _channel_fetch_message(self, last_id=False, limit=20):
|
||||
""" Return message values of the current channel.
|
||||
:param last_id : last message id to start the research
|
||||
@@ -1002,16 +1007,22 @@ class Channel(models.Model):
|
||||
"""
|
||||
notifications = []
|
||||
for channel in self:
|
||||
data = {
|
||||
data = dict({
|
||||
'channel_id': channel.id,
|
||||
'is_typing': is_typing,
|
||||
'partner_id': self.env.user.partner_id.id,
|
||||
'partner_name': self.env.user.partner_id.name,
|
||||
}
|
||||
}, **channel._notify_typing_partner_data())
|
||||
notifications.append([channel, 'mail.channel.partner/typing_status', data]) # notify backend users
|
||||
notifications.append([channel.uuid, 'mail.channel.partner/typing_status', data]) # notify frontend users
|
||||
self.env['bus.bus']._sendmany(notifications)
|
||||
|
||||
def _notify_typing_partner_data(self):
|
||||
"""Returns typing partner data for self channel."""
|
||||
self.ensure_one()
|
||||
return {
|
||||
'partner_id': self.env.user.partner_id.id,
|
||||
'partner_name': self.env.user.partner_id.name,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def channel_search_to_join(self, name=None, domain=None):
|
||||
""" Return the channel info of the channel the current partner can join
|
||||
|
||||
@@ -59,7 +59,12 @@
|
||||
<div class="o_Message_header ml-2">
|
||||
<t t-if="messageView.message.author">
|
||||
<div class="o_Message_authorName o_Message_authorRedirect o_redirect text-truncate" t-on-click="_onClickAuthorName" title="Open profile">
|
||||
<t t-esc="messageView.message.author.nameOrDisplayName"/>
|
||||
<t t-if="messageView.message.originThread">
|
||||
<t t-esc="messageView.message.originThread.getMemberName(messageView.message.author)"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-esc="messageView.message.author.nameOrDisplayName"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
<t t-elif="messageView.message.guestAuthor">
|
||||
|
||||
@@ -21,9 +21,12 @@ export class MessageSeenIndicator extends Component {
|
||||
return this.env._t("Seen by Everyone");
|
||||
}
|
||||
if (this.messageSeenIndicator.hasSomeoneSeen) {
|
||||
const partnersThatHaveSeen = this.messageSeenIndicator.partnersThatHaveSeen.map(
|
||||
partner => partner.name
|
||||
);
|
||||
const partnersThatHaveSeen = this.messageSeenIndicator.partnersThatHaveSeen.map(partner => {
|
||||
if (this.message.originThread) {
|
||||
return this.message.originThread.getMemberName(partner);
|
||||
}
|
||||
return partner.nameOrDisplayName;
|
||||
});
|
||||
if (partnersThatHaveSeen.length === 1) {
|
||||
return _.str.sprintf(
|
||||
this.env._t("Seen by %s"),
|
||||
@@ -47,9 +50,12 @@ export class MessageSeenIndicator extends Component {
|
||||
return this.env._t("Received by Everyone");
|
||||
}
|
||||
if (this.messageSeenIndicator.hasSomeoneFetched) {
|
||||
const partnersThatHaveFetched = this.messageSeenIndicator.partnersThatHaveFetched.map(
|
||||
partner => partner.name
|
||||
);
|
||||
const partnersThatHaveFetched = this.messageSeenIndicator.partnersThatHaveFetched.map(partner => {
|
||||
if (this.message.originThread) {
|
||||
return this.message.originThread.getMemberName(partner);
|
||||
}
|
||||
return partner.nameOrDisplayName;
|
||||
});
|
||||
if (partnersThatHaveFetched.length === 1) {
|
||||
return _.str.sprintf(
|
||||
this.env._t("Received by %s"),
|
||||
|
||||
@@ -55,7 +55,12 @@ registerModel({
|
||||
1: secondUser,
|
||||
2: thirdUser,
|
||||
} = [
|
||||
...this.partners.map(partner => partner.nameOrDisplayName),
|
||||
...this.partners.map(partner => {
|
||||
if (this.message.originThread) {
|
||||
return this.message.originThread.getMemberName(partner);
|
||||
}
|
||||
return partner.nameOrDisplayName;
|
||||
}),
|
||||
...this.guests.map(guest => guest.name),
|
||||
];
|
||||
switch (length) {
|
||||
|
||||
@@ -410,6 +410,15 @@ registerModel({
|
||||
compute: '_computeAvatarUrl',
|
||||
}),
|
||||
country: many2one('Country'),
|
||||
/**
|
||||
* Deprecated.
|
||||
* States the `display_name` of this partner, as returned by the server.
|
||||
* The value of this field is unreliable (notably its value depends on
|
||||
* context on which it was received) therefore it should only be used as
|
||||
* a default if the actual `name` is missing (@see `nameOrDisplayName`).
|
||||
* And if a specific name format is required, it should be computed from
|
||||
* relevant fields instead.
|
||||
*/
|
||||
display_name: attr({
|
||||
compute: '_computeDisplayName',
|
||||
default: "",
|
||||
|
||||
@@ -792,6 +792,15 @@ registerModel({
|
||||
this.endCall();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Returns the name of the given partner in the context of this thread.
|
||||
*
|
||||
* @param {mail.partner} partner
|
||||
* @returns {string}
|
||||
*/
|
||||
getMemberName(partner) {
|
||||
return partner.nameOrDisplayName;
|
||||
},
|
||||
/**
|
||||
* Returns the text that identifies this thread in a mention.
|
||||
*
|
||||
@@ -1679,20 +1688,20 @@ registerModel({
|
||||
if (this.orderedOtherTypingMembers.length === 1) {
|
||||
return _.str.sprintf(
|
||||
this.env._t("%s is typing..."),
|
||||
this.orderedOtherTypingMembers[0].nameOrDisplayName
|
||||
this.getMemberName(this.orderedOtherTypingMembers[0])
|
||||
);
|
||||
}
|
||||
if (this.orderedOtherTypingMembers.length === 2) {
|
||||
return _.str.sprintf(
|
||||
this.env._t("%s and %s are typing..."),
|
||||
this.orderedOtherTypingMembers[0].nameOrDisplayName,
|
||||
this.orderedOtherTypingMembers[1].nameOrDisplayName
|
||||
this.getMemberName(this.orderedOtherTypingMembers[0]),
|
||||
this.getMemberName(this.orderedOtherTypingMembers[1])
|
||||
);
|
||||
}
|
||||
return _.str.sprintf(
|
||||
this.env._t("%s, %s and more are typing..."),
|
||||
this.orderedOtherTypingMembers[0].nameOrDisplayName,
|
||||
this.orderedOtherTypingMembers[1].nameOrDisplayName
|
||||
this.getMemberName(this.orderedOtherTypingMembers[0]),
|
||||
this.getMemberName(this.orderedOtherTypingMembers[1])
|
||||
);
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -620,25 +620,19 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'members': [
|
||||
{
|
||||
'active': True,
|
||||
'display_name': 'Ernest Employee',
|
||||
'email': 'e.e@example.com',
|
||||
'email': False,
|
||||
'id': self.users[0].partner_id.id,
|
||||
'im_status': 'leave_offline',
|
||||
'is_internal_user': True,
|
||||
'im_status': False,
|
||||
'livechat_username': False,
|
||||
'name': 'Ernest Employee',
|
||||
'out_of_office_date_end': self.leaves.filtered(lambda l: l.employee_id.user_id == self.users[0]).date_to.strftime(DEFAULT_SERVER_DATE_FORMAT),
|
||||
'user_id': self.users[0].id,
|
||||
},
|
||||
{
|
||||
'active': True,
|
||||
'display_name': 'test1',
|
||||
'email': 'test1@example.com',
|
||||
'email': False,
|
||||
'id': self.users[1].partner_id.id,
|
||||
'im_status': 'leave_offline',
|
||||
'is_internal_user': True,
|
||||
'im_status': False,
|
||||
'livechat_username': False,
|
||||
'name': 'test1',
|
||||
'out_of_office_date_end': self.leaves.filtered(lambda l: l.employee_id.user_id == self.users[1]).date_to.strftime(DEFAULT_SERVER_DATE_FORMAT),
|
||||
'user_id': self.users[1].id,
|
||||
},
|
||||
],
|
||||
'message_needaction_counter': 0,
|
||||
@@ -689,25 +683,19 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'members': [
|
||||
{
|
||||
'active': False,
|
||||
'display_name': 'Public user',
|
||||
'email': False,
|
||||
'id': self.env.ref('base.public_partner').id,
|
||||
'im_status': 'im_partner',
|
||||
'is_internal_user': False,
|
||||
'im_status': False,
|
||||
'livechat_username': False,
|
||||
'name': 'Public user',
|
||||
'out_of_office_date_end': False,
|
||||
'user_id': False,
|
||||
},
|
||||
{
|
||||
'active': True,
|
||||
'display_name': 'Ernest Employee',
|
||||
'email': 'e.e@example.com',
|
||||
'email': False,
|
||||
'id': self.users[0].partner_id.id,
|
||||
'im_status': 'leave_offline',
|
||||
'is_internal_user': True,
|
||||
'im_status': False,
|
||||
'livechat_username': False,
|
||||
'name': 'Ernest Employee',
|
||||
'out_of_office_date_end': self.leaves.filtered(lambda l: l.employee_id.user_id == self.users[0]).date_to.strftime(DEFAULT_SERVER_DATE_FORMAT),
|
||||
'user_id': self.users[0].id,
|
||||
},
|
||||
],
|
||||
'message_needaction_counter': 0,
|
||||
|
||||
@@ -49,7 +49,8 @@ class Website(models.Model):
|
||||
"id": chat_request_channel.id,
|
||||
"operator_pid": [
|
||||
chat_request_channel.livechat_operator_id.id,
|
||||
chat_request_channel.livechat_operator_id.display_name
|
||||
chat_request_channel.livechat_operator_id.user_livechat_username or chat_request_channel.livechat_operator_id.display_name,
|
||||
chat_request_channel.livechat_operator_id.user_livechat_username,
|
||||
],
|
||||
"name": chat_request_channel.name,
|
||||
"uuid": chat_request_channel.uuid,
|
||||
|
||||
Reference in New Issue
Block a user