[REM] mail: remove "email_send" field on the mail channel
Purpose ======= Remove the "email_send" field on the <mail.channel> (mass_mailing named on the JS side). This feature will be introduced with a new model (<mail.group>) in a new module in the next commit. Remove the email notification support on the channel, so now the mail channels work only by chat. Remove the "subject" on the Discuss side because this was used only on "email" channel. Technical ========= In the mail channel model we can drop the usage of the blacklist as well as the usage of the "email_to" field. Those two features were mainly used for mailing list and have no utility for "chat like" channel. Links ===== Task-2510267 See odoo/odoo/pull/71599 See odoo/enterprise/pull/19296 See odoo/upgrade/pull/2600
This commit is contained in:
committed by
Thibault Delavallée
parent
59d6bb23d7
commit
9183a86fde
@@ -47,7 +47,6 @@ class TestOutOfOffice(TestHrHolidaysCommon):
|
||||
'channel_partner_ids': [(4, partner.id), (4, partner2.id)],
|
||||
'public': 'private',
|
||||
'channel_type': 'chat',
|
||||
'email_send': False,
|
||||
'name': 'test'
|
||||
})
|
||||
channel_info = channel.channel_info()[0]
|
||||
|
||||
@@ -130,7 +130,6 @@ class ImLivechatChannel(models.Model):
|
||||
'channel_type': 'livechat',
|
||||
'name': ' '.join([visitor_user.display_name if visitor_user else anonymous_name, operator.livechat_username if operator.livechat_username else operator.name]),
|
||||
'public': 'private',
|
||||
'email_send': False,
|
||||
}
|
||||
|
||||
def _open_livechat_mail_channel(self, anonymous_name, previous_operator_id=None, user_id=None, country_id=None):
|
||||
|
||||
@@ -212,7 +212,6 @@ MockServer.include({
|
||||
'channel_type': 'livechat',
|
||||
'name': membersName.join(' '),
|
||||
'public': 'private',
|
||||
'mass_mailing': false, // email_send
|
||||
};
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,6 @@ class Channel(models.Model):
|
||||
string='Channel Type', default='channel')
|
||||
is_chat = fields.Boolean(string='Is a chat', compute='_compute_is_chat')
|
||||
description = fields.Text('Description')
|
||||
email_send = fields.Boolean('Send messages by email', default=False)
|
||||
image_128 = fields.Image("Image", max_width=128, max_height=128, default=_get_default_image)
|
||||
channel_partner_ids = fields.Many2many(
|
||||
'res.partner', string='Members',
|
||||
@@ -244,10 +243,9 @@ class Channel(models.Model):
|
||||
# channel_info is called before actually unpinning the channel
|
||||
channel_info['is_pinned'] = False
|
||||
self.env['bus.bus'].sendone((self._cr.dbname, 'res.partner', partner.id), channel_info)
|
||||
if not self.email_send:
|
||||
notification = _('<div class="o_mail_notification">left <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>', self.id, self.name)
|
||||
# post 'channel left' message as root since the partner just unsubscribed from the channel
|
||||
self.sudo().message_post(body=notification, subtype_xmlid="mail.mt_comment", author_id=partner.id)
|
||||
notification = _('<div class="o_mail_notification">left <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>', self.id, self.name)
|
||||
# post 'channel left' message as root since the partner just unsubscribed from the channel
|
||||
self.sudo().message_post(body=notification, subtype_xmlid="mail.mt_comment", author_id=partner.id)
|
||||
return result
|
||||
|
||||
def _action_add_members(self, partners):
|
||||
@@ -366,7 +364,6 @@ class Channel(models.Model):
|
||||
"""
|
||||
# get values from msg_vals or from message if msg_vals doen't exists
|
||||
msg_sudo = message.sudo()
|
||||
mailing_channels = self.filtered('email_send')
|
||||
message_type = msg_vals.get('message_type', 'email') if msg_vals else msg_sudo.message_type
|
||||
pids = msg_vals.get('partner_ids', []) if msg_vals else msg_sudo.partner_ids.ids
|
||||
|
||||
@@ -374,40 +371,14 @@ class Channel(models.Model):
|
||||
if message_type not in ('comment', 'email'):
|
||||
return []
|
||||
# notify only mailing lists or if mentioning recipients
|
||||
if not mailing_channels and not pids:
|
||||
if not pids:
|
||||
return []
|
||||
|
||||
email_from = tools.email_normalize(msg_vals.get('email_from') or msg_sudo.email_from)
|
||||
author_id = msg_vals.get('author_id') or msg_sudo.author_id.id
|
||||
|
||||
recipients_data = []
|
||||
if mailing_channels:
|
||||
sql_query = """
|
||||
SELECT DISTINCT ON (partner.id) partner.id
|
||||
FROM res_partner partner
|
||||
LEFT JOIN mail_channel_partner mcp on partner.id = mcp.partner_id
|
||||
LEFT JOIN mail_channel channel on channel.id = mcp.channel_id
|
||||
LEFT JOIN res_users users on partner.id = users.partner_id
|
||||
WHERE (users.notification_type != 'inbox' or users.id IS NULL)
|
||||
AND (partner.email != %s or partner.email IS NULL)
|
||||
AND channel.id = ANY(%s)
|
||||
AND partner.id != ANY(%s)"""
|
||||
self.env.cr.execute(
|
||||
sql_query,
|
||||
(email_from, mailing_channels.ids, [author_id] if author_id else [], )
|
||||
)
|
||||
for partner_id, in self._cr.fetchall():
|
||||
# ocn_client: will add partners to recipient recipient_data. more ocn notifications. We neeed to filter them maybe
|
||||
recipients_data.append({
|
||||
'id': partner_id,
|
||||
'share': True,
|
||||
'active': True,
|
||||
'notif': 'email',
|
||||
'type': 'channel_email',
|
||||
'groups': []
|
||||
})
|
||||
remaining = [pid for pid in pids if pid not in [recipient['id'] for recipient in recipients_data]]
|
||||
if remaining:
|
||||
if pids:
|
||||
sql_query = """
|
||||
SELECT DISTINCT ON (partner.id) partner.id,
|
||||
partner.partner_share,
|
||||
@@ -419,7 +390,7 @@ class Channel(models.Model):
|
||||
AND partner.id = ANY(%s) AND partner.id != ANY(%s)"""
|
||||
self.env.cr.execute(
|
||||
sql_query,
|
||||
(email_from, remaining, [author_id] if author_id else [], )
|
||||
(email_from, list(pids), [author_id] if author_id else [], )
|
||||
)
|
||||
for partner_id, partner_share, notif in self._cr.fetchall():
|
||||
# ocn_client: will add partners to recipient recipient_data. more ocn notifications. We neeed to filter them maybe
|
||||
@@ -460,17 +431,6 @@ class Channel(models.Model):
|
||||
headers['X-Forge-To'] = list_to
|
||||
return headers
|
||||
|
||||
def _notify_email_recipient_values(self, recipient_ids):
|
||||
# Excluded Blacklisted
|
||||
whitelist = self.env['res.partner'].sudo().browse(recipient_ids).filtered(lambda p: not p.is_blacklisted)
|
||||
# real mailing list: multiple recipients (hidden by X-Forge-To)
|
||||
if self.alias_domain and self.alias_name:
|
||||
return {
|
||||
'email_to': ','.join(formataddr((partner.name, partner.email_normalized)) for partner in whitelist if partner.email_normalized),
|
||||
'recipient_ids': [],
|
||||
}
|
||||
return super(Channel, self)._notify_email_recipient_values(whitelist.ids)
|
||||
|
||||
def _notify_thread(self, message, msg_vals=False, **kwargs):
|
||||
# link message to channel
|
||||
rdata = super(Channel, self)._notify_thread(message, msg_vals=msg_vals, **kwargs)
|
||||
@@ -610,7 +570,6 @@ class Channel(models.Model):
|
||||
'is_minimized': False,
|
||||
'channel_type': channel.channel_type,
|
||||
'public': channel.public,
|
||||
'mass_mailing': channel.email_send,
|
||||
'group_based_subscription': bool(channel.group_ids),
|
||||
'create_uid': channel.create_uid.id,
|
||||
}
|
||||
@@ -715,7 +674,6 @@ class Channel(models.Model):
|
||||
'channel_partner_ids': [Command.link(partner_id) for partner_id in partners_to],
|
||||
'public': 'private',
|
||||
'channel_type': 'chat',
|
||||
'email_send': False,
|
||||
'name': ', '.join(self.env['res.partner'].sudo().browse(partners_to).mapped('name')),
|
||||
})
|
||||
channel._broadcast(partners_to)
|
||||
@@ -918,7 +876,7 @@ class Channel(models.Model):
|
||||
def channel_join_and_get_info(self):
|
||||
self.ensure_one()
|
||||
added = self.action_follow()
|
||||
if added and self.channel_type == 'channel' and not self.email_send:
|
||||
if added and self.channel_type == 'channel':
|
||||
notification = _('<div class="o_mail_notification">joined <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>', self.id, self.name)
|
||||
self.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment")
|
||||
|
||||
@@ -938,7 +896,6 @@ class Channel(models.Model):
|
||||
new_channel = self.create({
|
||||
'name': name,
|
||||
'public': privacy,
|
||||
'email_send': False,
|
||||
})
|
||||
notification = _('<div class="o_mail_notification">created <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>', new_channel.id, new_channel.name)
|
||||
new_channel.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment")
|
||||
|
||||
@@ -100,7 +100,6 @@ class Followers(models.Model):
|
||||
self.env['res.users'].flush(['notification_type', 'active', 'partner_id', 'groups_id'])
|
||||
self.env['res.partner'].flush(['active', 'partner_share'])
|
||||
self.env['res.groups'].flush(['users'])
|
||||
self.env['mail.channel'].flush(['email_send', 'channel_type'])
|
||||
if records and subtype_id:
|
||||
query = """
|
||||
SELECT DISTINCT ON (pid) * FROM (
|
||||
|
||||
@@ -36,7 +36,6 @@ export class ChatWindow extends Component {
|
||||
isDeviceMobile: this.env.messaging.device.isMobile,
|
||||
localeTextDirection: this.env.messaging.locale.textDirection,
|
||||
thread,
|
||||
threadMassMailing: thread && thread.mass_mailing,
|
||||
threadModel: thread && thread.model,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<ThreadView
|
||||
class="o_ChatWindow_thread"
|
||||
composerAttachmentsDetailsMode="'card'"
|
||||
hasComposer="chatWindow.thread.model !== 'mail.box' and (!chatWindow.thread.mass_mailing or env.messaging.device.isMobile)"
|
||||
hasComposer="chatWindow.thread.model !== 'mail.box'"
|
||||
hasComposerCurrentPartnerAvatar="false"
|
||||
hasComposerSendButton="env.messaging.device.isMobile"
|
||||
hasSquashCloseMessages="chatWindow.thread.model !== 'mail.box'"
|
||||
|
||||
@@ -31,7 +31,6 @@ export class ChatWindowHeader extends Component {
|
||||
isDeviceMobile: this.env.messaging.device.isMobile,
|
||||
thread,
|
||||
threadLocalMessageUnreadCounter: thread && thread.localMessageUnreadCounter,
|
||||
threadMassMailing: thread && thread.mass_mailing,
|
||||
threadModel: thread && thread.model,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
<div class="o_ChatWindowHeader_item o_ChatWindowHeader_name" t-att-title="chatWindow.name">
|
||||
<t t-esc="chatWindow.name"/>
|
||||
</div>
|
||||
<t t-if="chatWindow.thread and chatWindow.thread.mass_mailing">
|
||||
<i class="fa fa-envelope-o" title="Messages are sent by email" role="img"/>
|
||||
</t>
|
||||
<t t-if="chatWindow.thread and chatWindow.thread.localMessageUnreadCounter > 0">
|
||||
<div class="o_ChatWindowHeader_counter o_ChatWindowHeader_item">
|
||||
(<t t-esc="chatWindow.thread.localMessageUnreadCounter"/>)
|
||||
|
||||
@@ -1899,7 +1899,6 @@ QUnit.test('chat window: post message on non-mailing channel with "CTRL-Enter" k
|
||||
this.data['mail.channel'].records.push({
|
||||
id: 20,
|
||||
is_minimized: true,
|
||||
mass_mailing: false,
|
||||
});
|
||||
await this.start({
|
||||
env: {
|
||||
|
||||
@@ -52,12 +52,10 @@ export class Composer extends Component {
|
||||
composerCanPostMessage: composer && composer.canPostMessage,
|
||||
composerHasFocus: composer && composer.hasFocus,
|
||||
composerIsLog: composer && composer.isLog,
|
||||
composerSubjectContent: composer && composer.subjectContent,
|
||||
isDeviceMobile: this.env.messaging.device.isMobile,
|
||||
thread,
|
||||
threadChannelType: thread && thread.channel_type, // for livechat override
|
||||
threadDisplayName: thread && thread.displayName,
|
||||
threadMassMailing: thread && thread.mass_mailing,
|
||||
threadModel: thread && thread.model,
|
||||
threadName: thread && thread.name,
|
||||
};
|
||||
@@ -81,10 +79,6 @@ export class Composer extends Component {
|
||||
* Reference of the text input component.
|
||||
*/
|
||||
this._textInputRef = useRef('textInput');
|
||||
/**
|
||||
* Reference of the subject input. Useful to set content.
|
||||
*/
|
||||
this._subjectRef = useRef('subject');
|
||||
this._onClickCaptureGlobal = this._onClickCaptureGlobal.bind(this);
|
||||
}
|
||||
|
||||
@@ -231,9 +225,6 @@ export class Composer extends Component {
|
||||
if (!this.composer) {
|
||||
return;
|
||||
}
|
||||
if (this._subjectRef.el) {
|
||||
this._subjectRef.el.value = this.composer.subjectContent;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -340,13 +331,6 @@ export class Composer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onInputSubject() {
|
||||
this.composer.update({ subjectContent: this._subjectRef.el.value });
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {KeyboardEvent} ev
|
||||
|
||||
@@ -114,20 +114,6 @@
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.o_Composer_subject {
|
||||
border-top: $border-width solid $border-color;
|
||||
border-right: $border-width solid $border-color;
|
||||
border-left: $border-width solid $border-color;
|
||||
border-radius: $o-mail-rounded-rectangle-border-radius-sm $o-mail-rounded-rectangle-border-radius-sm 0 0;
|
||||
}
|
||||
|
||||
.o_Composer_subjectInput {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
padding: map-get($spacers, 2) map-get($spacers, 3);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.o_Composer_textInput {
|
||||
flex: 1 1 auto;
|
||||
align-self: stretch;
|
||||
@@ -212,11 +198,7 @@
|
||||
.o_Composer_coreMain:not(.o-composer-is-compact) {
|
||||
background: $white;
|
||||
border: $border-width solid $border-color;
|
||||
|
||||
// textarea should be all rounded but only when there is no subject field above
|
||||
&:not(.o-composer-is-extended) {
|
||||
border-radius: $o-mail-rounded-rectangle-border-radius-lg;
|
||||
}
|
||||
border-radius: $o-mail-rounded-rectangle-border-radius-lg;
|
||||
}
|
||||
|
||||
.o_Composer_currentPartnerAvatar {
|
||||
|
||||
@@ -52,11 +52,6 @@
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="composer.thread and composer.thread.model === 'mail.channel' and composer.thread.mass_mailing">
|
||||
<div class="o_Composer_subject">
|
||||
<input class="o_Composer_subjectInput" type="text" placeholder="Subject" t-on-input="_onInputSubject" t-ref="subject"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="props.hasCurrentPartnerAvatar">
|
||||
<div class="o_Composer_sidebarMain">
|
||||
<img class="o_Composer_currentPartnerAvatar rounded-circle" t-att-src="currentPartnerAvatar" alt=""/>
|
||||
@@ -66,7 +61,6 @@
|
||||
class="o_Composer_coreMain"
|
||||
t-att-class="{
|
||||
'o-composer-is-compact': props.isCompact,
|
||||
'o-composer-is-extended': composer.thread and composer.thread.mass_mailing,
|
||||
}"
|
||||
>
|
||||
<ComposerTextInput
|
||||
|
||||
@@ -197,30 +197,6 @@ QUnit.test('composer text input placeholder should contain correspondent name wh
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('mailing channel composer: basic rendering', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// channel that is expected to be rendered, with proper mass_mailing
|
||||
// value and a random unique id that will be referenced in the test
|
||||
this.data['mail.channel'].records.push({ id: 20, mass_mailing: true });
|
||||
await this.start();
|
||||
const thread = this.env.models['mail.thread'].findFromIdentifyingData({
|
||||
id: 20,
|
||||
model: 'mail.channel',
|
||||
});
|
||||
await this.createComposerComponent(thread.composer);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_ComposerTextInput',
|
||||
"Composer should have a text input"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_Composer_subjectInput',
|
||||
"Composer should have a subject input"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('add an emoji', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
@@ -1609,81 +1585,6 @@ QUnit.test('composer text input cleared on message post', async function (assert
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('composer inputs cleared on message post in composer of a mailing channel', async function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
// channel that is expected to be rendered, with proper mass_mailing
|
||||
// value and a random unique id that will be referenced in the test
|
||||
this.data['mail.channel'].records.push({ id: 20, mass_mailing: true });
|
||||
await this.start({
|
||||
async mockRPC(route, args) {
|
||||
if (args.method === 'message_post') {
|
||||
assert.step('message_post');
|
||||
assert.ok(
|
||||
'body' in args.kwargs,
|
||||
"body should be posted with the message"
|
||||
);
|
||||
assert.strictEqual(
|
||||
args.kwargs.body,
|
||||
"test message",
|
||||
"posted body should be the one typed in text input"
|
||||
);
|
||||
assert.ok(
|
||||
'subject' in args.kwargs,
|
||||
"subject should be posted with the message"
|
||||
);
|
||||
assert.strictEqual(
|
||||
args.kwargs.subject,
|
||||
"test subject",
|
||||
"posted subject should be the one typed in subject input"
|
||||
);
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
const thread = this.env.models['mail.thread'].findFromIdentifyingData({
|
||||
id: 20,
|
||||
model: 'mail.channel',
|
||||
});
|
||||
await this.createComposerComponent(thread.composer);
|
||||
// Type message
|
||||
await afterNextRender(() => {
|
||||
document.querySelector(`.o_ComposerTextInput_textarea`).focus();
|
||||
document.execCommand('insertText', false, "test message");
|
||||
});
|
||||
assert.strictEqual(
|
||||
document.querySelector(`.o_ComposerTextInput_textarea`).value,
|
||||
"test message",
|
||||
"should have inserted text content in editable"
|
||||
);
|
||||
|
||||
await afterNextRender(() => {
|
||||
document.querySelector(`.o_Composer_subjectInput`).focus();
|
||||
document.execCommand('insertText', false, "test subject");
|
||||
});
|
||||
assert.strictEqual(
|
||||
document.querySelector(`.o_Composer_subjectInput`).value,
|
||||
"test subject",
|
||||
"should have inserted text content in input"
|
||||
);
|
||||
|
||||
// Send message
|
||||
await afterNextRender(() =>
|
||||
document.querySelector('.o_Composer_buttonSend').click()
|
||||
);
|
||||
assert.verifySteps(['message_post']);
|
||||
assert.strictEqual(
|
||||
document.querySelector(`.o_ComposerTextInput_textarea`).value,
|
||||
"",
|
||||
"should have no content in composer input after posting message"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector(`.o_Composer_subjectInput`).value,
|
||||
"",
|
||||
"should have no content in composer subject input after posting message"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('composer with thread typing notification status', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
|
||||
@@ -559,18 +559,6 @@ QUnit.test('sidebar: channel rendering with needaction counter', async function
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('sidebar: mailing channel', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
// channel that is expected to be in the sidebar, with proper mass_mailing value
|
||||
this.data['mail.channel'].records.push({ mass_mailing: true });
|
||||
await this.start();
|
||||
assert.containsOnce(
|
||||
document.querySelector(`.o_DiscussSidebar_groupChannel .o_DiscussSidebar_item`),
|
||||
'.fa.fa-envelope-o',
|
||||
"should have an icon to indicate that the channel is a mailing channel"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('sidebar: public/private channel rendering', async function (assert) {
|
||||
assert.expect(5);
|
||||
@@ -2882,12 +2870,12 @@ QUnit.test('post a simple message', async function (assert) {
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('post message on non-mailing channel with "Enter" keyboard shortcut', async function (assert) {
|
||||
QUnit.test('post message on channel with "Enter" keyboard shortcut', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// channel expected to be found in the sidebar
|
||||
// with a random unique id that will be referenced in the test
|
||||
this.data['mail.channel'].records.push({ id: 20, mass_mailing: false });
|
||||
this.data['mail.channel'].records.push({ id: 20 });
|
||||
await this.start({
|
||||
discuss: {
|
||||
params: {
|
||||
@@ -2917,7 +2905,7 @@ QUnit.test('post message on non-mailing channel with "Enter" keyboard shortcut',
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('do not post message on non-mailing channel with "SHIFT-Enter" keyboard shortcut', async function (assert) {
|
||||
QUnit.test('do not post message on channel with "SHIFT-Enter" keyboard shortcut', async function (assert) {
|
||||
// Note that test doesn't assert SHIFT-Enter makes a newline, because this
|
||||
// default browser cannot be simulated with just dispatching
|
||||
// programmatically crafted events...
|
||||
@@ -2925,7 +2913,7 @@ QUnit.test('do not post message on non-mailing channel with "SHIFT-Enter" keyboa
|
||||
|
||||
// channel expected to be found in the sidebar
|
||||
// with a random unique id that will be referenced in the test
|
||||
this.data['mail.channel'].records.push({ id: 20, mass_mailing: true });
|
||||
this.data['mail.channel'].records.push({ id: 20 });
|
||||
await this.start({
|
||||
discuss: {
|
||||
params: {
|
||||
@@ -2953,114 +2941,6 @@ QUnit.test('do not post message on non-mailing channel with "SHIFT-Enter" keyboa
|
||||
"should still not have any message in channel after pressing 'Shift-Enter' in text input of composer"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('post message on mailing channel with "CTRL-Enter" keyboard shortcut', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// channel expected to be found in the sidebar
|
||||
// with a random unique id that will be referenced in the test
|
||||
this.data['mail.channel'].records.push({ id: 20, mass_mailing: true });
|
||||
await this.start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: 'mail.channel_20',
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_Message',
|
||||
"should not have any message initially in channel"
|
||||
);
|
||||
|
||||
// insert some HTML in editable
|
||||
await afterNextRender(() => {
|
||||
document.querySelector(`.o_ComposerTextInput_textarea`).focus();
|
||||
document.execCommand('insertText', false, "Test");
|
||||
});
|
||||
await afterNextRender(() => {
|
||||
const kevt = new window.KeyboardEvent('keydown', { ctrlKey: true, key: "Enter" });
|
||||
document.querySelector('.o_ComposerTextInput_textarea').dispatchEvent(kevt);
|
||||
});
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_Message',
|
||||
"should now have single message in channel after posting message from pressing 'CTRL-Enter' in text input of composer"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('post message on mailing channel with "META-Enter" keyboard shortcut', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// channel expected to be found in the sidebar
|
||||
// with a random unique id that will be referenced in the test
|
||||
this.data['mail.channel'].records.push({ id: 20, mass_mailing: true });
|
||||
await this.start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: 'mail.channel_20',
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_Message',
|
||||
"should not have any message initially in channel"
|
||||
);
|
||||
|
||||
// insert some HTML in editable
|
||||
await afterNextRender(() => {
|
||||
document.querySelector(`.o_ComposerTextInput_textarea`).focus();
|
||||
document.execCommand('insertText', false, "Test");
|
||||
});
|
||||
await afterNextRender(() => {
|
||||
const kevt = new window.KeyboardEvent('keydown', { key: "Enter", metaKey: true });
|
||||
document.querySelector('.o_ComposerTextInput_textarea').dispatchEvent(kevt);
|
||||
});
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_Message',
|
||||
"should now have single message in channel after posting message from pressing 'META-Enter' in text input of composer"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('do not post message on mailing channel with "Enter" keyboard shortcut', async function (assert) {
|
||||
// Note that test doesn't assert Enter makes a newline, because this
|
||||
// default browser cannot be simulated with just dispatching
|
||||
// programmatically crafted events...
|
||||
assert.expect(2);
|
||||
|
||||
// channel expected to be found in the sidebar
|
||||
// with a random unique id that will be referenced in the test
|
||||
this.data['mail.channel'].records.push({ id: 20, mass_mailing: true });
|
||||
await this.start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: 'mail.channel_20',
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_Message',
|
||||
"should not have any message initially in mailing channel"
|
||||
);
|
||||
|
||||
// insert some HTML in editable
|
||||
await afterNextRender(() => {
|
||||
document.querySelector(`.o_ComposerTextInput_textarea`).focus();
|
||||
document.execCommand('insertText', false, "Test");
|
||||
});
|
||||
const kevt = new window.KeyboardEvent('keydown', { key: "Enter" });
|
||||
document.querySelector('.o_ComposerTextInput_textarea').dispatchEvent(kevt);
|
||||
await nextAnimationFrame();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_Message',
|
||||
"should still not have any message in mailing channel after pressing 'Enter' in text input of composer"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('rendering of inbox message', async function (assert) {
|
||||
// AKU TODO: kinda message specific test
|
||||
assert.expect(7);
|
||||
|
||||
@@ -35,7 +35,6 @@ export class DiscussSidebarItem extends Component {
|
||||
threadDisplayName: thread && thread.displayName,
|
||||
threadGroupBasedSubscription: thread && thread.group_based_subscription,
|
||||
threadLocalMessageUnreadCounter: thread && thread.localMessageUnreadCounter,
|
||||
threadMassMailing: thread && thread.mass_mailing,
|
||||
threadMessageNeedactionCounter: thread && thread.message_needaction_counter,
|
||||
threadModel: thread && thread.model,
|
||||
};
|
||||
|
||||
@@ -28,9 +28,6 @@
|
||||
<div class="o_DiscussSidebarItem_item o_DiscussSidebarItem_name" t-att-class="{ 'o-item-unread': thread.localMessageUnreadCounter > 0 }">
|
||||
<t t-esc="thread.displayName"/>
|
||||
</div>
|
||||
<t t-if="thread.mass_mailing">
|
||||
<i class="fa fa-envelope-o" title="Messages are sent by email" role="img"/>
|
||||
</t>
|
||||
</t>
|
||||
<div class="o-autogrow o_DiscussSidebarItem_item"/>
|
||||
<t t-if="thread.model !== 'mail.box'">
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
hasCurrentPartnerAvatar="props.hasComposerCurrentPartnerAvatar"
|
||||
hasSendButton="props.hasComposerSendButton"
|
||||
hasThreadTyping="props.hasComposerThreadTyping"
|
||||
isCompact="(threadView.thread.model === 'mail.channel' and threadView.thread.mass_mailing) ? false : undefined"
|
||||
isDoFocus="props.isDoFocus"
|
||||
showAttachmentsExtensions="props.showComposerAttachmentsExtensions"
|
||||
showAttachmentsFilenames="props.showComposerAttachmentsFilenames"
|
||||
|
||||
@@ -221,9 +221,6 @@ function factory(dependencies) {
|
||||
message_type: 'comment',
|
||||
partner_ids: this.recipients.map(partner => partner.id),
|
||||
};
|
||||
if (this.subjectContent) {
|
||||
postData.subject = this.subjectContent;
|
||||
}
|
||||
try {
|
||||
let messageId;
|
||||
this.update({ isPostingMessage: true });
|
||||
@@ -760,7 +757,6 @@ function factory(dependencies) {
|
||||
isLastStateChangeProgrammatic: true,
|
||||
mentionedChannels: unlinkAll(),
|
||||
mentionedPartners: unlinkAll(),
|
||||
subjectContent: "",
|
||||
textInputContent: '',
|
||||
textInputCursorEnd: 0,
|
||||
textInputCursorStart: 0,
|
||||
@@ -990,12 +986,6 @@ function factory(dependencies) {
|
||||
threadSuggestedRecipientInfoListIsSelected: attr({
|
||||
related: 'threadSuggestedRecipientInfoList.isSelected',
|
||||
}),
|
||||
/**
|
||||
* Composer subject input content.
|
||||
*/
|
||||
subjectContent: attr({
|
||||
default: "",
|
||||
}),
|
||||
/**
|
||||
* States which type of suggestion is currently in progress, if any.
|
||||
* The value of this field contains the magic char that corresponds to
|
||||
|
||||
-17
@@ -205,23 +205,6 @@ function factory(dependencies) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Message from mailing channel should not make a notification in
|
||||
// Odoo for users with notification "Handled by Email".
|
||||
// Channel has been marked as read server-side in this case, so
|
||||
// it should not display a notification by incrementing the
|
||||
// unread counter.
|
||||
if (
|
||||
channel.mass_mailing &&
|
||||
this.env.session.notification_type === 'email'
|
||||
) {
|
||||
this._handleNotificationChannelSeen(channelId, {
|
||||
last_message_id: messageData.id,
|
||||
partner_id: this.env.messaging.currentPartner.id,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// In all other cases: update counter and notify if necessary
|
||||
|
||||
// Chat from OdooBot is considered disturbing and should only be
|
||||
// shown on the menu, but no notification and no thread open.
|
||||
const isChatWithOdooBot = (
|
||||
|
||||
@@ -1168,9 +1168,6 @@ function factory(dependencies) {
|
||||
if (this.model !== 'mail.channel') {
|
||||
return false;
|
||||
}
|
||||
if (this.mass_mailing) {
|
||||
return false;
|
||||
}
|
||||
return ['chat', 'livechat'].includes(this.channel_type);
|
||||
}
|
||||
|
||||
@@ -1797,7 +1794,6 @@ function factory(dependencies) {
|
||||
default: false,
|
||||
dependencies: [
|
||||
'channel_type',
|
||||
'mass_mailing',
|
||||
'model',
|
||||
],
|
||||
}),
|
||||
@@ -1932,9 +1928,6 @@ function factory(dependencies) {
|
||||
mainCache: one2one('mail.thread_cache', {
|
||||
compute: '_computeMainCache',
|
||||
}),
|
||||
mass_mailing: attr({
|
||||
default: false,
|
||||
}),
|
||||
members: many2many('mail.partner', {
|
||||
inverse: 'memberThreads',
|
||||
}),
|
||||
|
||||
@@ -82,12 +82,11 @@ function factory(dependencies) {
|
||||
if (!this.thread) {
|
||||
return;
|
||||
}
|
||||
const isMailingList = this.thread.model === 'mail.channel' && this.thread.mass_mailing;
|
||||
// Actually in mobile there is a send button, so we need there 'enter' to allow new line.
|
||||
// Hence, we want to use a different shortcut 'ctrl/meta enter' to send for small screen
|
||||
// size with a non-mailing channel.
|
||||
// here send will be done on clicking the button or using the 'ctrl/meta enter' shortcut.
|
||||
if (this.env.messaging.device.isMobile || isMailingList) {
|
||||
if (this.env.messaging.device.isMobile) {
|
||||
return ['ctrl-enter', 'meta-enter'];
|
||||
}
|
||||
return ['enter'];
|
||||
@@ -340,7 +339,6 @@ function factory(dependencies) {
|
||||
'device',
|
||||
'deviceIsMobile',
|
||||
'thread',
|
||||
'threadMassMailing',
|
||||
'threadModel',
|
||||
],
|
||||
}),
|
||||
@@ -394,12 +392,6 @@ function factory(dependencies) {
|
||||
default: {},
|
||||
related: 'threadViewer.threadCacheInitialScrollPositions',
|
||||
}),
|
||||
/**
|
||||
* Serves as compute dependency.
|
||||
*/
|
||||
threadMassMailing: attr({
|
||||
related: 'thread.mass_mailing',
|
||||
}),
|
||||
/**
|
||||
* Serves as compute dependency.
|
||||
*/
|
||||
|
||||
@@ -78,8 +78,6 @@ export class MockModels {
|
||||
is_minimized: { string: "isMinimized", type: "boolean", default: false },
|
||||
// In python this belongs to mail.channel.partner. Here for simplicity.
|
||||
is_pinned: { string: "isPinned", type: "boolean", default: true },
|
||||
// In python: email_send.
|
||||
mass_mailing: { string: "Send messages by email", type: "boolean", default: false },
|
||||
members: { string: "Members", type: 'many2many', relation: 'res.partner', default() { return [this.currentPartnerId]; } },
|
||||
message_unread_counter: { string: "# unread messages", type: 'integer' },
|
||||
name: { string: "Name", type: "char", required: true },
|
||||
|
||||
@@ -686,7 +686,6 @@ MockServer.include({
|
||||
// an existing chat.
|
||||
const id = this._mockCreate('mail.channel', {
|
||||
channel_type: 'chat',
|
||||
mass_mailing: false,
|
||||
is_minimized: true,
|
||||
is_pinned: true,
|
||||
members: [[6, 0, partners_to]],
|
||||
|
||||
@@ -150,7 +150,6 @@ class TestChannelInternals(MailCommon):
|
||||
cls.test_channel = cls.env['mail.channel'].with_context(cls._test_context).create({
|
||||
'name': 'Test',
|
||||
'channel_type': 'channel',
|
||||
'email_send': False,
|
||||
'description': 'Description',
|
||||
'alias_name': 'test',
|
||||
'public': 'public',
|
||||
@@ -216,7 +215,6 @@ class TestChannelInternals(MailCommon):
|
||||
""" Posting a message on a chat should not send emails """
|
||||
self.test_channel.write({
|
||||
'channel_type': 'chat',
|
||||
'email_send': False
|
||||
})
|
||||
self.test_channel._action_add_members(self.partner_employee | self.partner_admin | self.test_partner)
|
||||
with self.mock_mail_gateway():
|
||||
@@ -229,29 +227,6 @@ class TestChannelInternals(MailCommon):
|
||||
self.assertEqual(new_msg.partner_ids, self.env['res.partner'])
|
||||
self.assertEqual(new_msg.notified_partner_ids, self.env['res.partner'])
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
|
||||
def test_channel_recipients_mailing_list(self):
|
||||
""" Posting a message on a mailing list should send one email to all recipients """
|
||||
self.test_channel.write({'email_send': True})
|
||||
(self.user_admin | self.user_employee).write({'notification_type': 'email'})
|
||||
|
||||
# Subscribe an user without email. We shouldn't try to send email to them.
|
||||
self.test_channel._action_add_members(self.partner_employee | self.partner_admin | self.test_partner | self.partner_employee_nomail)
|
||||
with self.mock_mail_gateway():
|
||||
with self.with_user('employee'):
|
||||
channel = self.env['mail.channel'].browse(self.test_channel.ids)
|
||||
channel.message_post(body="Test", message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
self.assertSentEmail(self.partner_employee, [self.partner_admin, self.test_partner])
|
||||
|
||||
# test mailing with with inbox notification type
|
||||
self.user_admin.write({'notification_type': 'inbox'})
|
||||
self.user_admin.flush()
|
||||
with self.mock_mail_gateway():
|
||||
with self.with_user('employee'):
|
||||
channel = self.env['mail.channel'].browse(self.test_channel.ids)
|
||||
channel.message_post(body="Test", message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
self.assertSentEmail(self.partner_employee, [self.test_partner])
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
|
||||
def test_channel_recipients_mention(self):
|
||||
""" Posting a message on a classic channel should support mentioning somebody """
|
||||
@@ -395,7 +370,6 @@ class TestChannelInternals(MailCommon):
|
||||
even if `_action_unfollow()` is called again.
|
||||
'''
|
||||
channel = self.env['mail.channel'].browse(self.test_channel.id)
|
||||
channel.write({'email_send': False})
|
||||
channel._action_add_members(self.test_partner)
|
||||
|
||||
# no message should be posted under test_partner's name
|
||||
@@ -434,71 +408,3 @@ class TestChannelInternals(MailCommon):
|
||||
allowed_company_ids=self.company_admin.ids
|
||||
).channel_get(self.partner_employee_c2.ids)
|
||||
self.assertTrue(initial_channel_info, 'should be able to chat with multi company user')
|
||||
|
||||
def test_multi_company_message_post_notifications(self):
|
||||
company_1 = self.company_admin
|
||||
company_2 = self.env['res.company'].create({'name': 'Company 2'})
|
||||
|
||||
# Company 1 and notification_type == "inbox"
|
||||
user_1 = self.user_employee
|
||||
|
||||
# Company 1 and notification_type == "email"
|
||||
user_2 = self.user_admin
|
||||
user_2.notification_type = 'email'
|
||||
|
||||
user_3 = mail_new_test_user(
|
||||
self.env, login='user3', email='user3@example.com', groups='base.group_user',
|
||||
company_id=company_2.id, company_ids=[(6, 0, company_2.ids)],
|
||||
name='user3', notification_type='inbox')
|
||||
|
||||
user_4 = mail_new_test_user(
|
||||
self.env, login='user4', email='user4@example.com', groups='base.group_user',
|
||||
company_id=company_2.id, company_ids=[(6, 0, company_2.ids)],
|
||||
name='user4', notification_type='email')
|
||||
|
||||
partner_without_user = self.env['res.partner'].create({
|
||||
'name': 'Partner',
|
||||
'email': 'partner_test_123@example.com',
|
||||
})
|
||||
mail_channel = self.env['mail.channel'].with_user(user_1).create({
|
||||
'name': 'Channel',
|
||||
'channel_partner_ids': [
|
||||
(4, user_1.partner_id.id),
|
||||
(4, user_2.partner_id.id),
|
||||
(4, user_3.partner_id.id),
|
||||
(4, user_4.partner_id.id),
|
||||
(4, partner_without_user.id),
|
||||
],
|
||||
'email_send': True,
|
||||
})
|
||||
|
||||
mail_channel.invalidate_cache()
|
||||
(user_1 | user_2 | user_3 | user_4).invalidate_cache()
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
mail_channel.with_user(user_1).with_company(company_1).message_post(
|
||||
body='Test body message 1337',
|
||||
message_type='email',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
|
||||
self.assertSentEmail(user_1.partner_id, [user_2.partner_id])
|
||||
self.assertSentEmail(user_1.partner_id, [user_4.partner_id])
|
||||
self.assertEqual(len(self._mails), 3, 'Should have send only 3 emails to user 2, user 4 and the partner')
|
||||
|
||||
self.assertBusNotifications([(self.cr.dbname, 'mail.channel', mail_channel.id)])
|
||||
|
||||
# Should not create mail notifications for user 1 & 3
|
||||
self.assertFalse(self.env['mail.notification'].search([('res_partner_id', '=', user_1.partner_id.id)]))
|
||||
self.assertFalse(self.env['mail.notification'].search([('res_partner_id', '=', user_3.partner_id.id)]))
|
||||
|
||||
# Should create mail notifications for user 2 & 4
|
||||
self.assertTrue(self.env['mail.notification'].search([('res_partner_id', '=', user_2.partner_id.id)]))
|
||||
self.assertTrue(self.env['mail.notification'].search([('res_partner_id', '=', user_4.partner_id.id)]))
|
||||
|
||||
# Check that we did not send a "channel_seen" notifications
|
||||
# for the users which receive the notifications by email
|
||||
notification_seen_user_2 = self.env['bus.bus'].search([('create_uid', '=', user_2.id)])
|
||||
self.assertFalse(notification_seen_user_2, 'Should not have sent a notification as user 2')
|
||||
notification_seen_user_4 = self.env['bus.bus'].search([('create_uid', '=', user_4.id)])
|
||||
self.assertFalse(notification_seen_user_4, 'Should not have sent a notification as user 4')
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
</div>
|
||||
<group class="o_label_nowrap">
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="email_send"/>
|
||||
<field name="description" placeholder="Topics discussed in this group..."/>
|
||||
</group>
|
||||
<group name="group_alias" attrs="{'invisible': [('alias_domain', '=', False)]}">
|
||||
|
||||
@@ -140,44 +140,6 @@ class TestMassMailFeatures(MassMailCommon, CronMixinCase):
|
||||
super(TestMassMailFeatures, cls).setUpClass()
|
||||
cls._create_mailing_list()
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_channel_blacklisted_recipients(self):
|
||||
""" Posting a message on a channel should send one email to all recipients, except the blacklisted ones """
|
||||
test_channel = self.env['mail.channel'].create({
|
||||
'name': 'Test',
|
||||
'description': 'Description',
|
||||
'alias_name': 'test',
|
||||
'public': 'public',
|
||||
'email_send': True,
|
||||
})
|
||||
test_partner = self.env['res.partner'].create({
|
||||
'name': 'Test Partner',
|
||||
'email': 'test@example.com',
|
||||
})
|
||||
|
||||
blacklisted_partner = self.env['res.partner'].create({
|
||||
'name': 'Blacklisted Partner',
|
||||
'email': 'test@black.list',
|
||||
})
|
||||
|
||||
# Set Blacklist
|
||||
self.env['mail.blacklist'].create({
|
||||
'email': 'test@black.list',
|
||||
})
|
||||
|
||||
test_channel._action_add_members(test_partner + blacklisted_partner)
|
||||
with self.mock_mail_gateway():
|
||||
test_channel.message_post(body="Test", message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
|
||||
self.assertEqual(len(self._mails), 1, 'Number of mail incorrect. Should be equal to 1.')
|
||||
for email in self._mails:
|
||||
self.assertEqual(
|
||||
set(email['email_to']),
|
||||
set([formataddr((test_partner.name, test_partner.email))]),
|
||||
'email_to incorrect. Should be equal to "%s"' % (
|
||||
formataddr((test_partner.name, test_partner.email))))
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_mailing_cron_trigger(self):
|
||||
|
||||
@@ -69,7 +69,6 @@ class WebsiteVisitor(models.Model):
|
||||
'livechat_operator_id': self.env.user.partner_id.id,
|
||||
'channel_type': 'livechat',
|
||||
'public': 'private',
|
||||
'email_send': False,
|
||||
'country_id': country.id,
|
||||
'anonymous_name': visitor_name,
|
||||
'name': ', '.join([visitor_name, operator.livechat_username if operator.livechat_username else operator.name]),
|
||||
|
||||
Reference in New Issue
Block a user