Move model definition files from `models/thing/thing.js` to
`models/thing.js` and remove the now empty intermediate directory.
Task-2759582
* = calendar, crm_livechat, hr, im_livechat, mail_bot, sms, snailmail,
website_livechat, website_slides
closes odoo/odoo#86464
Related: odoo/enterprise#25291
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
199 lines
6.8 KiB
JavaScript
199 lines
6.8 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { registerModel } from '@mail/model/model_core';
|
|
import { attr, one } from '@mail/model/model_field';
|
|
import { insert, unlink } from '@mail/model/model_field_command';
|
|
|
|
registerModel({
|
|
name: 'User',
|
|
identifyingFields: ['id'],
|
|
modelMethods: {
|
|
/**
|
|
* @param {Object} data
|
|
* @returns {Object}
|
|
*/
|
|
convertData(data) {
|
|
const data2 = {};
|
|
if ('id' in data) {
|
|
data2.id = data.id;
|
|
}
|
|
if ('partner_id' in data) {
|
|
if (!data.partner_id) {
|
|
data2.partner = unlink();
|
|
} else {
|
|
const partnerNameGet = data['partner_id'];
|
|
const partnerData = {
|
|
display_name: partnerNameGet[1],
|
|
id: partnerNameGet[0],
|
|
};
|
|
data2.partner = insert(partnerData);
|
|
}
|
|
}
|
|
return data2;
|
|
},
|
|
/**
|
|
* Performs the `read` RPC on `res.users`.
|
|
*
|
|
* @param {Object} param0
|
|
* @param {Object} param0.context
|
|
* @param {string[]} param0.fields
|
|
* @param {integer[]} param0.ids
|
|
*/
|
|
async performRpcRead({ context, fields, ids }) {
|
|
const usersData = await this.env.services.rpc({
|
|
model: 'res.users',
|
|
method: 'read',
|
|
args: [ids],
|
|
kwargs: {
|
|
context,
|
|
fields,
|
|
},
|
|
}, { shadow: true });
|
|
return this.messaging.models['User'].insert(usersData.map(userData =>
|
|
this.messaging.models['User'].convertData(userData)
|
|
));
|
|
},
|
|
},
|
|
recordMethods: {
|
|
/**
|
|
* Fetches the partner of this user.
|
|
*/
|
|
async fetchPartner() {
|
|
return this.messaging.models['User'].performRpcRead({
|
|
ids: [this.id],
|
|
fields: ['partner_id'],
|
|
context: { active_test: false },
|
|
});
|
|
},
|
|
/**
|
|
* Gets the chat between this user and the current user.
|
|
*
|
|
* If a chat is not appropriate, a notification is displayed instead.
|
|
*
|
|
* @returns {Thread|undefined}
|
|
*/
|
|
async getChat() {
|
|
if (!this.partner) {
|
|
await this.async(() => this.fetchPartner());
|
|
}
|
|
if (!this.partner) {
|
|
// This user has been deleted from the server or never existed:
|
|
// - Validity of id is not verified at insert.
|
|
// - There is no bus notification in case of user delete from
|
|
// another tab or by another user.
|
|
this.env.services['notification'].notify({
|
|
message: this.env._t("You can only chat with existing users."),
|
|
type: 'warning',
|
|
});
|
|
return;
|
|
}
|
|
// in other cases a chat would be valid, find it or try to create it
|
|
let chat = this.messaging.models['Thread'].find(thread =>
|
|
thread.channel_type === 'chat' &&
|
|
thread.correspondent === this.partner &&
|
|
thread.model === 'mail.channel' &&
|
|
thread.public === 'private'
|
|
);
|
|
if (!chat || !chat.isPinned) {
|
|
// if chat is not pinned then it has to be pinned client-side
|
|
// and server-side, which is a side effect of following rpc
|
|
chat = await this.async(() =>
|
|
this.messaging.models['Thread'].performRpcCreateChat({
|
|
partnerIds: [this.partner.id],
|
|
})
|
|
);
|
|
}
|
|
if (!chat) {
|
|
this.env.services['notification'].notify({
|
|
message: this.env._t("An unexpected error occurred during the creation of the chat."),
|
|
type: 'warning',
|
|
});
|
|
return;
|
|
}
|
|
return chat;
|
|
},
|
|
/**
|
|
* Opens a chat between this user and the current user and returns it.
|
|
*
|
|
* If a chat is not appropriate, a notification is displayed instead.
|
|
*
|
|
* @param {Object} [options] forwarded to @see `Thread:open()`
|
|
* @returns {Thread|undefined}
|
|
*/
|
|
async openChat(options) {
|
|
const chat = await this.async(() => this.getChat());
|
|
if (!chat) {
|
|
return;
|
|
}
|
|
await this.async(() => chat.open(options));
|
|
return chat;
|
|
},
|
|
/**
|
|
* Opens the most appropriate view that is a profile for this user.
|
|
* Because user is a rather technical model to allow login, it's the
|
|
* partner profile that contains the most useful information.
|
|
*
|
|
* @override
|
|
*/
|
|
async openProfile() {
|
|
if (!this.partner) {
|
|
await this.async(() => this.fetchPartner());
|
|
}
|
|
if (!this.partner) {
|
|
// This user has been deleted from the server or never existed:
|
|
// - Validity of id is not verified at insert.
|
|
// - There is no bus notification in case of user delete from
|
|
// another tab or by another user.
|
|
this.env.services['notification'].notify({
|
|
message: this.env._t("You can only open the profile of existing users."),
|
|
type: 'warning',
|
|
});
|
|
return;
|
|
}
|
|
return this.partner.openProfile();
|
|
},
|
|
/**
|
|
* @private
|
|
* @returns {string|undefined}
|
|
*/
|
|
_computeDisplayName() {
|
|
return this.display_name || this.partner && this.partner.display_name;
|
|
},
|
|
/**
|
|
* @private
|
|
* @returns {string|undefined}
|
|
*/
|
|
_computeNameOrDisplayName() {
|
|
return this.partner && this.partner.nameOrDisplayName || this.display_name;
|
|
},
|
|
},
|
|
fields: {
|
|
id: attr({
|
|
readonly: true,
|
|
required: true,
|
|
}),
|
|
/**
|
|
* Determines whether this user is an internal user. An internal user is
|
|
* a member of the group `base.group_user`. This is the inverse of the
|
|
* `share` field in python.
|
|
*/
|
|
isInternalUser: attr(),
|
|
display_name: attr({
|
|
compute: '_computeDisplayName',
|
|
}),
|
|
model: attr({
|
|
default: 'res.user',
|
|
}),
|
|
nameOrDisplayName: attr({
|
|
compute: '_computeNameOrDisplayName',
|
|
}),
|
|
partner: one('Partner', {
|
|
inverse: 'user',
|
|
}),
|
|
/**
|
|
* Id of this user's res.users.settings record.
|
|
*/
|
|
resUsersSettingsId: attr(),
|
|
},
|
|
});
|