738 lines
27 KiB
JavaScript
738 lines
27 KiB
JavaScript
/* @odoo-module */
|
|
|
|
import { serializeDateTime } from "@web/core/l10n/dates";
|
|
import { patch } from "@web/core/utils/patch";
|
|
import { MockServer } from "@web/../tests/helpers/mock_server";
|
|
|
|
const { DateTime } = luxon;
|
|
|
|
patch(MockServer.prototype, {
|
|
/**
|
|
* @override
|
|
*/
|
|
async performRPC(route, args) {
|
|
if (route === "/mail/attachment/upload") {
|
|
const ufile = args.body.get("ufile");
|
|
const is_pending = args.body.get("is_pending") === "true";
|
|
const model = is_pending ? "mail.compose.message" : args.body.get("thread_model");
|
|
const id = is_pending ? 0 : parseInt(args.body.get("thread_id"));
|
|
const attachmentId = this.mockCreate("ir.attachment", {
|
|
// datas,
|
|
mimetype: ufile.type,
|
|
name: ufile.name,
|
|
res_id: id,
|
|
res_model: model,
|
|
});
|
|
if (args.body.get("voice")) {
|
|
this.mockCreate("discuss.voice.metadata", { attachment_id: attachmentId });
|
|
}
|
|
return this._mockIrAttachment_attachmentFormat([attachmentId])[0];
|
|
}
|
|
return super.performRPC(...arguments);
|
|
},
|
|
/**
|
|
* @override
|
|
*/
|
|
async _performRPC(route, args) {
|
|
if (route === "/mail/attachment/delete") {
|
|
const { attachment_id } = args;
|
|
return this._mockRouteMailAttachmentRemove(attachment_id);
|
|
}
|
|
if (route === "/discuss/channel/messages") {
|
|
const { search_term, channel_id, after, around, before, limit } = args;
|
|
return this._mockRouteDiscussChannelMessages(
|
|
channel_id,
|
|
search_term,
|
|
before,
|
|
after,
|
|
around,
|
|
limit
|
|
);
|
|
}
|
|
if (route === "/discuss/channel/mute") {
|
|
const { channel_id, minutes } = args;
|
|
return this._mockRouteDiscussChannelMute(channel_id, minutes);
|
|
}
|
|
if (route === "/discuss/channel/pinned_messages") {
|
|
const { channel_id } = args;
|
|
return this._mockRouteDiscussChannelPins(channel_id);
|
|
}
|
|
if (route === "/discuss/channel/notify_typing") {
|
|
const id = args.channel_id;
|
|
const is_typing = args.is_typing;
|
|
return this._mockRouteDiscussChannelNotifyTyping(id, is_typing);
|
|
}
|
|
if (new RegExp("/discuss/channel/\\d+/partner/\\d+/avatar_128").test(route)) {
|
|
return;
|
|
}
|
|
if (route === "/discuss/channel/ping") {
|
|
return;
|
|
}
|
|
if (route === "/discuss/channel/members") {
|
|
const { channel_id, known_member_ids } = args;
|
|
return this._mockDiscussChannelloadOlderMembers([channel_id], known_member_ids);
|
|
}
|
|
if (route === "/mail/history/messages") {
|
|
const { search_term, after, before, limit } = args;
|
|
return this._mockRouteMailMessageHistory(search_term, after, before, limit);
|
|
}
|
|
if (route === "/mail/init_messaging") {
|
|
return this._mockRouteMailInitMessaging();
|
|
}
|
|
if (route === "/mail/inbox/messages") {
|
|
const { search_term, after, around, before, limit } = args;
|
|
return this._mockRouteMailMessageInbox(search_term, after, before, around, limit);
|
|
}
|
|
if (route === "/mail/link_preview") {
|
|
return this._mockRouteMailLinkPreview(args.message_id, args.clear);
|
|
}
|
|
if (route === "/mail/link_preview/delete") {
|
|
const linkPreviews = this.pyEnv["mail.link.preview"].searchRead([
|
|
["id", "in", args.link_preview_ids],
|
|
]);
|
|
for (const linkPreview of linkPreviews) {
|
|
this.pyEnv["bus.bus"]._sendone(
|
|
this._mockMailMessage__busNotificationTarget(linkPreview.message_id[0]),
|
|
"mail.link.preview/delete",
|
|
{
|
|
id: linkPreview.id,
|
|
message_id: linkPreview.message_id[0],
|
|
}
|
|
);
|
|
}
|
|
return args;
|
|
}
|
|
if (route === "/mail/load_message_failures") {
|
|
return this._mockRouteMailLoadMessageFailures();
|
|
}
|
|
if (route === "/mail/message/post") {
|
|
const finalData = {};
|
|
for (const allowedField of [
|
|
"attachment_ids",
|
|
"body",
|
|
"message_type",
|
|
"partner_ids",
|
|
"subtype_xmlid",
|
|
"parent_id",
|
|
"partner_emails",
|
|
"partner_additional_values",
|
|
]) {
|
|
if (args.post_data[allowedField] !== undefined) {
|
|
finalData[allowedField] = args.post_data[allowedField];
|
|
}
|
|
}
|
|
if (args.thread_model === "discuss.channel") {
|
|
return this._mockDiscussChannelMessagePost(args.thread_id, finalData, args.context);
|
|
}
|
|
return this._mockMailThreadMessagePost(
|
|
args.thread_model,
|
|
[args.thread_id],
|
|
finalData,
|
|
args.context
|
|
);
|
|
}
|
|
if (route === "/mail/message/reaction") {
|
|
return this._mockRouteMailMessageReaction(args);
|
|
}
|
|
if (route === "/mail/message/update_content") {
|
|
this.pyEnv["mail.message"].write([args.message_id], {
|
|
body: args.body,
|
|
attachment_ids: args.attachment_ids,
|
|
});
|
|
this.pyEnv["bus.bus"]._sendone(
|
|
this._mockMailMessage__busNotificationTarget(args.message_id),
|
|
"mail.record/insert",
|
|
{
|
|
Message: {
|
|
id: args.message_id,
|
|
body: args.body,
|
|
attachments: this._mockIrAttachment_attachmentFormat(args.attachment_ids),
|
|
},
|
|
}
|
|
);
|
|
return this._mockMailMessageMessageFormat([args.message_id])[0];
|
|
}
|
|
if (route === "/mail/partner/from_email") {
|
|
return this._mockRouteMailPartnerFromEmail(args.emails, args.additional_values);
|
|
}
|
|
if (route === "/mail/read_subscription_data") {
|
|
const follower_id = args.follower_id;
|
|
return this._mockRouteMailReadSubscriptionData(follower_id);
|
|
}
|
|
if (route === "/mail/rtc/channel/join_call") {
|
|
return this._mockRouteMailRtcChannelJoinCall(
|
|
args.channel_id,
|
|
args.check_rtc_session_ids
|
|
);
|
|
}
|
|
if (route === "/mail/rtc/channel/leave_call") {
|
|
return this._mockRouteMailRtcChannelLeaveCall(args.channel_id);
|
|
}
|
|
if (route === "/mail/rtc/session/update_and_broadcast") {
|
|
return this._mockRouteMailRtcSessionUpdateAndBroadcast(args.session_id, args.values);
|
|
}
|
|
if (route === "/mail/starred/messages") {
|
|
const { search_term, after, before, limit } = args;
|
|
return this._mockRouteMailMessageStarredMessages(search_term, after, before, limit);
|
|
}
|
|
if (route === "/mail/thread/data") {
|
|
return this._mockRouteMailThreadData(
|
|
args.thread_model,
|
|
args.thread_id,
|
|
args.request_list
|
|
);
|
|
}
|
|
if (route === "/mail/thread/messages") {
|
|
const { search_term, after, around, before, limit, thread_model, thread_id } = args;
|
|
return this._mockRouteMailThreadFetchMessages(
|
|
thread_model,
|
|
thread_id,
|
|
search_term,
|
|
before,
|
|
after,
|
|
around,
|
|
limit
|
|
);
|
|
}
|
|
if (route === "/discuss/gif/favorites") {
|
|
return [[]];
|
|
}
|
|
return super._performRPC(route, args);
|
|
},
|
|
/**
|
|
* Simulates the `/discuss/channel/pinned_messages` route.
|
|
*/
|
|
_mockRouteDiscussChannelPins(channel_id) {
|
|
const messageIds = this.pyEnv["mail.message"].search([
|
|
["model", "=", "discuss.channel"],
|
|
["res_id", "=", channel_id],
|
|
["pinned_at", "!=", false],
|
|
]);
|
|
return this._mockMailMessageMessageFormat(messageIds);
|
|
},
|
|
/**
|
|
* Simulates the `/mail/init_messaging` route.
|
|
*
|
|
* @private
|
|
* @returns {Object}
|
|
*/
|
|
_mockRouteMailInitMessaging() {
|
|
if (this._mockMailGuest__getGuestFromContext() && this.pyEnv.currentUser?._is_public()) {
|
|
return this._mockMailGuest__initMessaging();
|
|
}
|
|
return this._mockResUsers_InitMessaging([this.pyEnv.currentUserId]);
|
|
},
|
|
/**
|
|
* Simulates the `/mail/attachment/delete` route.
|
|
*
|
|
* @private
|
|
* @param {integer} attachment_id
|
|
*/
|
|
async _mockRouteMailAttachmentRemove(attachment_id) {
|
|
this.pyEnv["bus.bus"]._sendone(this.pyEnv.currentPartner, "ir.attachment/delete", {
|
|
id: attachment_id,
|
|
});
|
|
return this.pyEnv["ir.attachment"].unlink([attachment_id]);
|
|
},
|
|
/**
|
|
* Simulates the `/discuss/channel/messages` route.
|
|
*
|
|
* @private
|
|
* @param {integer} channel_id
|
|
* @param {integer} limit
|
|
* @param {integer} before
|
|
* @param {integer} after
|
|
* @param {integer} around
|
|
* @returns {Object} list of messages
|
|
*/
|
|
async _mockRouteDiscussChannelMessages(
|
|
channel_id,
|
|
search_term = false,
|
|
before = false,
|
|
after = false,
|
|
around = false,
|
|
limit = 30
|
|
) {
|
|
const domain = [
|
|
["res_id", "=", channel_id],
|
|
["model", "=", "discuss.channel"],
|
|
["message_type", "!=", "user_notification"],
|
|
];
|
|
const res = this._mockMailMessage_MessageFetch(
|
|
domain,
|
|
search_term,
|
|
before,
|
|
after,
|
|
around,
|
|
limit
|
|
);
|
|
if (!around) {
|
|
this._mockMailMessageSetMessageDone(res.messages.map((message) => message.id));
|
|
}
|
|
return {
|
|
...res,
|
|
messages: this._mockMailMessageMessageFormat(res.messages.map((message) => message.id)),
|
|
};
|
|
},
|
|
/**
|
|
* Simulates the `/discuss/channel/mute` route.
|
|
*
|
|
* @private
|
|
* @param {integer} channel_id
|
|
* @param {integer} minutes
|
|
*/
|
|
_mockRouteDiscussChannelMute(channel_id, minutes) {
|
|
const member = this._mockDiscussChannelMember__getAsSudoFromContext(channel_id);
|
|
let mute_until_dt;
|
|
if (minutes === -1) {
|
|
mute_until_dt = serializeDateTime(DateTime.fromISO("9999-12-31T23:59:59"));
|
|
} else if (minutes) {
|
|
mute_until_dt = serializeDateTime(DateTime.now().plus({ minutes }));
|
|
} else {
|
|
mute_until_dt = false;
|
|
}
|
|
this.pyEnv["discuss.channel.member"].write([member.id], { mute_until_dt });
|
|
const channel_data = {
|
|
id: member.channel_id[0],
|
|
model: "discuss.channel",
|
|
mute_until_dt,
|
|
};
|
|
this.pyEnv["bus.bus"]._sendone(this.pyEnv.currentPartner, "mail.record/insert", {
|
|
Thread: channel_data,
|
|
});
|
|
return "dummy";
|
|
},
|
|
/**
|
|
* Simulates the `/discuss/channel/notify_typing` route.
|
|
*
|
|
* @private
|
|
* @param {integer} channel_id
|
|
* @param {integer} limit
|
|
* @param {Object} [context={}]
|
|
*/
|
|
async _mockRouteDiscussChannelNotifyTyping(channel_id, is_typing) {
|
|
const memberOfCurrentUser =
|
|
this._mockDiscussChannelMember__getAsSudoFromContext(channel_id);
|
|
if (!memberOfCurrentUser) {
|
|
return;
|
|
}
|
|
this._mockDiscussChannelMember_NotifyTyping([memberOfCurrentUser.id], is_typing);
|
|
},
|
|
/**
|
|
* Simulates the `/mail/load_message_failures` route.
|
|
*
|
|
* @private
|
|
* @returns {Object[]}
|
|
*/
|
|
_mockRouteMailLoadMessageFailures() {
|
|
return this._mockResPartner_MessageFetchFailed(this.pyEnv.currentPartnerId);
|
|
},
|
|
/**
|
|
* Simulates `/mail/link_preview` route.
|
|
*
|
|
* @private
|
|
* @param {integer} message_id
|
|
* @returns {Object}
|
|
*/
|
|
_mockRouteMailLinkPreview(message_id, clear = false) {
|
|
const linkPreviews = [];
|
|
const [message] = this.pyEnv["mail.message"].searchRead([["id", "=", message_id]]);
|
|
if (message.body.includes("https://make-link-preview.com")) {
|
|
if (clear) {
|
|
const [linkPreview] = this.pyEnv["mail.link.preview"].searchRead([
|
|
["message_id", "=", message_id],
|
|
]);
|
|
this.pyEnv["bus.bus"]._sendone(
|
|
this._mockMailMessage__busNotificationTarget(linkPreview.message_id),
|
|
"mail.link.preview/delete",
|
|
{
|
|
id: linkPreview.id,
|
|
message_id: linkPreview.message_id,
|
|
}
|
|
);
|
|
}
|
|
|
|
const linkPreviewId = this.pyEnv["mail.link.preview"].create({
|
|
message_id: message.id,
|
|
og_description: "test description",
|
|
og_title: "Article title",
|
|
og_type: "article",
|
|
source_url: "https://make-link-preview.com",
|
|
});
|
|
const [linkPreview] = this.pyEnv["mail.link.preview"].searchRead([
|
|
["id", "=", linkPreviewId],
|
|
]);
|
|
linkPreviews.push(this._mockMailLinkPreviewFormat(linkPreview));
|
|
this.pyEnv["bus.bus"]._sendone(
|
|
this._mockMailMessage__busNotificationTarget(message_id),
|
|
"mail.record/insert",
|
|
{ LinkPreview: linkPreviews }
|
|
);
|
|
}
|
|
},
|
|
/**
|
|
* Simulates `/mail/message/reaction` route.
|
|
*/
|
|
_mockRouteMailMessageReaction({ action, content, message_id }) {
|
|
return this._mockMailMessage_messageReaction(message_id, content, action);
|
|
},
|
|
/**
|
|
* Simulates the `/mail/history/messages` route.
|
|
*
|
|
* @private
|
|
* @returns {Object}
|
|
*/
|
|
_mockRouteMailMessageHistory(search_term = false, after = false, before = false, limit = 30) {
|
|
const domain = [["needaction", "=", false]];
|
|
const res = this._mockMailMessage_MessageFetch(
|
|
domain,
|
|
search_term,
|
|
before,
|
|
after,
|
|
false,
|
|
limit
|
|
);
|
|
const messagesWithNotification = res.messages.filter((message) => {
|
|
const notifs = this.pyEnv["mail.notification"].searchRead([
|
|
["mail_message_id", "=", message.id],
|
|
["is_read", "=", true],
|
|
["res_partner_id", "=", this.pyEnv.currentPartnerId],
|
|
]);
|
|
return notifs.length > 0;
|
|
});
|
|
|
|
return {
|
|
...res,
|
|
messages: this._mockMailMessageMessageFormat(
|
|
messagesWithNotification.map((message) => message.id)
|
|
),
|
|
};
|
|
},
|
|
/**
|
|
* Simulates the `/mail/inbox/messages` route.
|
|
*
|
|
* @private
|
|
* @returns {Object}
|
|
*/
|
|
_mockRouteMailMessageInbox(
|
|
search_term = false,
|
|
after = false,
|
|
before = false,
|
|
around = false,
|
|
limit = 30
|
|
) {
|
|
const domain = [["needaction", "=", true]];
|
|
const res = this._mockMailMessage_MessageFetch(
|
|
domain,
|
|
search_term,
|
|
before,
|
|
after,
|
|
around,
|
|
limit
|
|
);
|
|
return {
|
|
...res,
|
|
messages: this._mockMailMessageFormatPersonalize(
|
|
res.messages.map((message) => message.id)
|
|
),
|
|
};
|
|
},
|
|
/**
|
|
* Simulates the `/mail/starred/messages` route.
|
|
*
|
|
* @private
|
|
* @returns {Object}
|
|
*/
|
|
_mockRouteMailMessageStarredMessages(
|
|
search_term = false,
|
|
after = false,
|
|
before = false,
|
|
limit = 30
|
|
) {
|
|
const domain = [["starred_partner_ids", "in", [this.pyEnv.currentPartnerId]]];
|
|
const res = this._mockMailMessage_MessageFetch(
|
|
domain,
|
|
search_term,
|
|
before,
|
|
after,
|
|
false,
|
|
limit
|
|
);
|
|
return {
|
|
...res,
|
|
messages: this._mockMailMessageMessageFormat(res.messages.map((message) => message.id)),
|
|
};
|
|
},
|
|
/**
|
|
* Simulates the `/mail/partner/from_email` route.
|
|
*
|
|
* @private
|
|
* @param {string[]} emails
|
|
* @returns {Object[]} list of partner data
|
|
*/
|
|
_mockRouteMailPartnerFromEmail(emails, additional_values = {}) {
|
|
const partners = emails.map(
|
|
(email) => this.pyEnv["res.partner"].search([["email", "=", email]])[0]
|
|
);
|
|
for (const index in partners) {
|
|
if (!partners[index]) {
|
|
const email = emails[index];
|
|
partners[index] = this.pyEnv["res.partner"].create({
|
|
email,
|
|
name: email,
|
|
...(additional_values[email] || {}),
|
|
});
|
|
}
|
|
}
|
|
return partners.map((partner_id) => {
|
|
const partner = this.getRecords("res.partner", [["id", "=", partner_id]])[0];
|
|
return { id: partner_id, name: partner.name, email: partner.email };
|
|
});
|
|
},
|
|
/**
|
|
* Simulates the `/mail/read_subscription_data` route.
|
|
*
|
|
* @private
|
|
* @param {integer} follower_id
|
|
* @returns {Object[]} list of followed subtypes
|
|
*/
|
|
async _mockRouteMailReadSubscriptionData(follower_id) {
|
|
const follower = this.getRecords("mail.followers", [["id", "=", follower_id]])[0];
|
|
const subtypes = this.getRecords("mail.message.subtype", [
|
|
"&",
|
|
["hidden", "=", false],
|
|
"|",
|
|
["res_model", "=", follower.res_model],
|
|
["res_model", "=", false],
|
|
]);
|
|
const subtypes_list = subtypes.map((subtype) => {
|
|
const parent = this.getRecords("mail.message.subtype", [
|
|
["id", "=", subtype.parent_id],
|
|
])[0];
|
|
return {
|
|
default: subtype.default,
|
|
followed: follower.subtype_ids.includes(subtype.id),
|
|
id: subtype.id,
|
|
internal: subtype.internal,
|
|
name: subtype.name,
|
|
parent_model: parent ? parent.res_model : false,
|
|
res_model: subtype.res_model,
|
|
sequence: subtype.sequence,
|
|
};
|
|
});
|
|
// NOTE: server is also doing a sort here, not reproduced for simplicity
|
|
return subtypes_list;
|
|
},
|
|
/**
|
|
* Simulates the `/mail/rtc/channel/join_call` route.
|
|
*
|
|
* @private
|
|
* @param {integer} channel_id
|
|
* @returns {integer[]} [check_rtc_session_ids]
|
|
*/
|
|
async _mockRouteMailRtcChannelJoinCall(channel_id, check_rtc_session_ids = []) {
|
|
const memberOfCurrentUser =
|
|
this._mockDiscussChannelMember__getAsSudoFromContext(channel_id);
|
|
const sessionId = this.pyEnv["discuss.channel.rtc.session"].create({
|
|
channel_member_id: memberOfCurrentUser.id,
|
|
channel_id, // on the server, this is a related field from channel_member_id and not explicitly set
|
|
guest_id: memberOfCurrentUser.guest_id[0],
|
|
partner_id: memberOfCurrentUser.partner_id[0],
|
|
});
|
|
const channelMembers = this.getRecords("discuss.channel.member", [
|
|
["channel_id", "=", channel_id],
|
|
]);
|
|
const rtcSessions = this.getRecords("discuss.channel.rtc.session", [
|
|
["channel_member_id", "in", channelMembers.map((channelMember) => channelMember.id)],
|
|
]);
|
|
return {
|
|
iceServers: false,
|
|
rtcSessions: [
|
|
[
|
|
"ADD",
|
|
rtcSessions.map((rtcSession) =>
|
|
this._mockDiscussChannelRtcSession_DiscussChannelRtcSessionFormat(
|
|
rtcSession.id
|
|
)
|
|
),
|
|
],
|
|
],
|
|
sessionId: sessionId,
|
|
};
|
|
},
|
|
/**
|
|
* Simulates the `/mail/rtc/channel/leave_call` route.
|
|
*
|
|
* @private
|
|
* @param {integer} channelId
|
|
*/
|
|
async _mockRouteMailRtcChannelLeaveCall(channel_id) {
|
|
const channelMembers = this.getRecords("discuss.channel.member", [
|
|
["channel_id", "=", channel_id],
|
|
]);
|
|
const rtcSessions = this.getRecords("discuss.channel.rtc.session", [
|
|
["channel_member_id", "in", channelMembers.map((channelMember) => channelMember.id)],
|
|
]);
|
|
const notifications = [];
|
|
const channelInfo =
|
|
this._mockDiscussChannelRtcSession_DiscussChannelRtcSessionFormatByChannel(
|
|
rtcSessions.map((rtcSession) => rtcSession.id)
|
|
);
|
|
for (const [channelId, sessionsData] of Object.entries(channelInfo)) {
|
|
const channel = this.pyEnv["discuss.channel"].searchRead([
|
|
["id", "=", parseInt(channelId)],
|
|
])[0];
|
|
const notificationRtcSessions = sessionsData.map((sessionsDataPoint) => {
|
|
return { id: sessionsDataPoint.id };
|
|
});
|
|
notifications.push([
|
|
channel,
|
|
"discuss.channel/rtc_sessions_update",
|
|
{
|
|
id: Number(channelId), // JS object keys are strings, but the type from the server is number
|
|
rtcSessions: [["DELETE", notificationRtcSessions]],
|
|
},
|
|
]);
|
|
}
|
|
for (const rtcSession of rtcSessions) {
|
|
const target = rtcSession.guest_id
|
|
? this.pyEnv["mail.guest"].searchRead([["id", "=", rtcSession.guest_id]])[0]
|
|
: this.pyEnv["res.partner"].searchRead([["id", "=", rtcSession.partner_id]])[0];
|
|
notifications.push([
|
|
target,
|
|
"discuss.channel.rtc.session/ended",
|
|
{ sessionId: rtcSession.id },
|
|
]);
|
|
}
|
|
this.pyEnv["bus.bus"]._sendmany(notifications);
|
|
},
|
|
/**
|
|
* Simulates the `/mail/rtc/session/update_and_broadcast` route.
|
|
*
|
|
* @param {number} session_id
|
|
* @param {object} values
|
|
*/
|
|
async _mockRouteMailRtcSessionUpdateAndBroadcast(session_id, values) {
|
|
const [session] = this.pyEnv["discuss.channel.rtc.session"].searchRead([
|
|
["id", "=", session_id],
|
|
]);
|
|
const [currentChannelMember] = this.pyEnv["discuss.channel.member"].searchRead([
|
|
["id", "=", session.channel_member_id[0]],
|
|
]);
|
|
if (session && currentChannelMember.partner_id[0] === this.pyEnv.currentPartnerId) {
|
|
this._mockDiscussChannelRtcSession__updateAndBroadcast(session.id, values);
|
|
}
|
|
},
|
|
/**
|
|
* Simulates the `/mail/thread/data` route.
|
|
*
|
|
* @param {string} thread_model
|
|
* @param {integer} thread_id
|
|
* @param {string[]} request_list
|
|
* @returns {Object}
|
|
*/
|
|
async _mockRouteMailThreadData(thread_model, thread_id, request_list) {
|
|
const res = {
|
|
hasWriteAccess: true, // mimic user with write access by default
|
|
hasReadAccess: true,
|
|
};
|
|
const thread = this.pyEnv[thread_model].searchRead([["id", "=", thread_id]])[0];
|
|
if (!thread) {
|
|
res["hasReadAccess"] = false;
|
|
return res;
|
|
}
|
|
res["canPostOnReadonly"] = thread_model === "discuss.channel"; // model that have attr _mail_post_access='read'
|
|
if (request_list.includes("activities")) {
|
|
const activities = this.pyEnv["mail.activity"].searchRead([
|
|
["id", "in", thread.activity_ids || []],
|
|
]);
|
|
res["activities"] = this._mockMailActivityActivityFormat(
|
|
activities.map((activity) => activity.id)
|
|
);
|
|
}
|
|
if (request_list.includes("attachments")) {
|
|
const attachments = this.pyEnv["ir.attachment"].searchRead([
|
|
["res_id", "=", thread.id],
|
|
["res_model", "=", thread_model],
|
|
]); // order not done for simplicity
|
|
res["attachments"] = this._mockIrAttachment_attachmentFormat(
|
|
attachments.map((attachment) => attachment.id)
|
|
);
|
|
// Specific implementation of mail.thread.main.attachment
|
|
if (this.models[thread_model].fields["message_main_attachment_id"]) {
|
|
res["mainAttachment"] = thread.message_main_attachment_id
|
|
? { id: thread.message_main_attachment_id[0] }
|
|
: false;
|
|
}
|
|
}
|
|
if (request_list.includes("followers")) {
|
|
const domain = [
|
|
["res_id", "=", thread.id],
|
|
["res_model", "=", thread_model],
|
|
];
|
|
res["followersCount"] = (thread.message_follower_ids || []).length;
|
|
const selfFollower = this.pyEnv["mail.followers"].searchRead(
|
|
domain.concat([["partner_id", "=", this.pyEnv.currentPartnerId]])
|
|
)[0];
|
|
res["selfFollower"] = selfFollower
|
|
? this._mockMailFollowers_FormatForChatter(selfFollower.id)[0]
|
|
: false;
|
|
res["followers"] = this._mockMailThreadMessageGetFollowers(thread_model, [thread_id]);
|
|
res["recipientsCount"] = (thread.message_follower_ids || []).length - 1;
|
|
res["recipients"] = this._mockMailThreadMessageGetFollowers(
|
|
thread_model,
|
|
[thread_id],
|
|
undefined,
|
|
100,
|
|
{ filter_recipients: true }
|
|
);
|
|
}
|
|
if (request_list.includes("suggestedRecipients")) {
|
|
res["suggestedRecipients"] = this._mockMailThread_MessageGetSuggestedRecipients(
|
|
thread_model,
|
|
[thread.id]
|
|
)[thread_id];
|
|
}
|
|
return res;
|
|
},
|
|
/**
|
|
* Simulates the `/mail/thread/messages` route.
|
|
*
|
|
* @private
|
|
* @param {string} res_model
|
|
* @param {integer} res_id
|
|
* @param {integer} before
|
|
* @param {integer} after
|
|
* @param {integer} limit
|
|
* @returns {Object[]} list of messages
|
|
*/
|
|
async _mockRouteMailThreadFetchMessages(
|
|
res_model,
|
|
res_id,
|
|
search_term = false,
|
|
before = false,
|
|
after = false,
|
|
around = false,
|
|
limit = 30
|
|
) {
|
|
const domain = [
|
|
["res_id", "=", res_id],
|
|
["model", "=", res_model],
|
|
["message_type", "!=", "user_notification"],
|
|
];
|
|
const res = this._mockMailMessage_MessageFetch(
|
|
domain,
|
|
search_term,
|
|
before,
|
|
after,
|
|
around,
|
|
limit
|
|
);
|
|
this._mockMailMessageSetMessageDone(res.messages.map((message) => message.id));
|
|
return {
|
|
...res,
|
|
messages: this._mockMailMessageMessageFormat(res.messages.map((message) => message.id)),
|
|
};
|
|
},
|
|
});
|