[FIX] mail: fix crash when unknown member leaves channel

Before this commit, a crash occurred when an unknown member
left a channel.

Steps to reproduce:
- Open two browsers, one for admin, one for demo
- Ensures both users are on the general channel
- Execute the `/leave` command with demo
- A traceback appears on the admin browser

This issue occurs because the `action_unfollow` only sends
the channel member id through the bus but the client relies
on other keys without checking for their presence. This
commit fixes this issue.

closes odoo/odoo#128090

X-original-commit: f8452b2eb8f9243402984d78184ed494384a187c
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
Signed-off-by: Matthieu Stockbauer (tsm) <tsm@odoo.com>
This commit is contained in:
tsm-odoo
2023-07-12 07:40:11 +02:00
parent f85ddd5964
commit f5da99c0ad
3 changed files with 31 additions and 11 deletions
@@ -39,8 +39,8 @@ export class ChannelMemberService {
channelId: memberData.persona.guest ? memberData.channel.id : null,
});
}
member.threadId = memberData.threadId ?? member.threadId ?? memberData.channel.id;
if (!member.thread) {
member.threadId = memberData.threadId ?? member.threadId ?? memberData.channel?.id;
if (member.threadId && !member.thread) {
// this prevents cyclic dependencies between mail.thread and discuss.channel.member
this.env.bus.trigger("mail.thread/insert", {
id: member.threadId,
@@ -50,7 +50,7 @@ export class ChannelMemberService {
switch (command) {
case "insert":
{
if (!member.thread.channelMembers.includes(member)) {
if (member.thread && !member.thread.channelMembers.includes(member)) {
member.thread.channelMembers.push(member);
}
}
@@ -59,7 +59,9 @@ export class ChannelMemberService {
removeFromArray(this.store.channelMembers, member);
// eslint-disable-next-line no-fallthrough
case "insert-and-unlink":
removeFromArray(member.thread.channelMembers, member);
if (member.thread) {
removeFromArray(member.thread.channelMembers, member);
}
break;
}
}
@@ -486,6 +486,27 @@ QUnit.test("leave command on channel", async (assert) => {
assert.verifySteps(["You unsubscribed from general."]);
});
QUnit.test("Can handle leave notification from unknown member", async (assert) => {
const pyEnv = await startServer();
const userId = pyEnv["res.users"].create({ name: "Dobby" });
const partnerId = pyEnv["res.partner"].create({ name: "Dobby", user_ids: [userId] });
const channelId = pyEnv["discuss.channel"].create({
name: "general",
channel_member_ids: [
Command.create({ partner_id: pyEnv.currentPartnerId }),
Command.create({ partner_id: partnerId }),
],
});
const { env, openDiscuss } = await start();
await openDiscuss(channelId);
await env.services.orm.call("discuss.channel", "action_unfollow", [channelId], {
context: { mockedUserId: userId },
});
await click("button[title='Show Member List']");
assert.containsOnce($, ".o-discuss-ChannelMember:contains(Mitchell)");
assert.containsNone($, ".o-discuss-ChannelMember:contains(Dobby)");
});
QUnit.test("leave command on chat", async (assert) => {
const pyEnv = await startServer();
const partnerId = pyEnv["res.partner"].create({ name: "Chuck Norris" });
@@ -91,7 +91,7 @@ patch(MockServer.prototype, "mail/models/discuss_channel", {
return this._mockDiscussChannelCreateGroup(partners_to);
}
if (args.model === "discuss.channel" && args.method === "execute_command_leave") {
return this._mockDiscussChannelExecuteCommandLeave(args);
return this._mockDiscussChannelExecuteCommandLeave(args, args.kwargs?.context);
}
if (args.model === "discuss.channel" && args.method === "execute_command_who") {
return this._mockDiscussChannelExecuteCommandWho(args);
@@ -218,9 +218,6 @@ patch(MockServer.prototype, "mail/models/discuss_channel", {
["channel_id", "in", ids],
["partner_id", "=", partnerId],
]);
const formattedChannelMember = this._mockDiscussChannelMember_DiscussChannelMemberFormat([
channelMember.id,
])[0];
if (!channelMember) {
return true;
}
@@ -241,7 +238,7 @@ patch(MockServer.prototype, "mail/models/discuss_channel", {
this.pyEnv["bus.bus"]._sendone(channel, "mail.record/insert", {
Channel: {
id: channel.id,
channelMembers: [["insert-and-unlink", formattedChannelMember]],
channelMembers: [["insert-and-unlink", { id: channelMember.id }]],
memberCount: this.pyEnv["discuss.channel.member"].searchCount([
["channel_id", "=", channel.id],
]),
@@ -844,10 +841,10 @@ patch(MockServer.prototype, "mail/models/discuss_channel", {
*
* @private
*/
_mockDiscussChannelExecuteCommandLeave(args) {
_mockDiscussChannelExecuteCommandLeave(args, context = {}) {
const channel = this.getRecords("discuss.channel", [["id", "in", args.args[0]]])[0];
if (channel.channel_type === "channel") {
this._mockDiscussChannelActionUnfollow([channel.id]);
this._mockDiscussChannelActionUnfollow([channel.id], context);
} else {
this._mockDiscussChannelChannelPin([channel.id], false);
}