[IMP] mail, *: introduce identifyingMode and remove identifyingFields
* = calendar, hr, im_livechat, rating, snailmail, website_livechat This simplifies definition and overrides and opens up new identification opportunities. Part of task-2741982 closes odoo/odoo#98166 Related: odoo/enterprise#30470 Signed-off-by: Louis Wicket (wil) <wil@odoo.com>
This commit is contained in:
committed by
Louis Wicket (wil)
parent
62520fbefe
commit
60c87dd426
@@ -9,7 +9,6 @@ import { getLangTimeFormat } from 'web.time';
|
||||
|
||||
registerModel({
|
||||
name: 'calendar.event',
|
||||
identifyingFields: ['id'],
|
||||
recordMethods: {
|
||||
_computeFormattedStart() {
|
||||
if (!this.start) {
|
||||
@@ -25,6 +24,7 @@ registerModel({
|
||||
compute: '_computeFormattedStart',
|
||||
}),
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insert } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'Employee',
|
||||
identifyingFields: ['id'],
|
||||
modelMethods: {
|
||||
/**
|
||||
* @param {Object} data
|
||||
@@ -164,6 +163,7 @@ registerModel({
|
||||
* Unique identifier for this employee.
|
||||
*/
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'Chatbot',
|
||||
identifyingFields: ['publicLivechatGlobalOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -157,6 +156,7 @@ registerModel({
|
||||
default: 3500, // in milliseconds
|
||||
}),
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
identifying: true,
|
||||
inverse: 'chatbot',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -5,9 +5,9 @@ import { attr, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'ChatbotStep',
|
||||
identifyingFields: ['chabotOwner'],
|
||||
fields: {
|
||||
chabotOwner: one('Chatbot', {
|
||||
identifying: true,
|
||||
inverse: 'currentStep',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { addFields, patchIdentifyingFields, patchRecordMethods } from '@mail/model/model_core';
|
||||
import { addFields, patchRecordMethods } from '@mail/model/model_core';
|
||||
import { one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
// ensure that the model definition is loaded before the patch
|
||||
@@ -8,15 +8,12 @@ import '@mail/models/discuss_sidebar_category';
|
||||
|
||||
addFields('DiscussSidebarCategory', {
|
||||
discussAsLivechat: one('Discuss', {
|
||||
identifying: true,
|
||||
inverse: 'categoryLivechat',
|
||||
readonly: true,
|
||||
}),
|
||||
});
|
||||
|
||||
patchIdentifyingFields('DiscussSidebarCategory', identifyingFields => {
|
||||
identifyingFields[0].push('discussAsLivechat');
|
||||
});
|
||||
|
||||
patchRecordMethods('DiscussSidebarCategory', {
|
||||
/**
|
||||
* @override
|
||||
|
||||
@@ -11,7 +11,6 @@ import { get_cookie, set_cookie, unaccent } from 'web.utils';
|
||||
|
||||
registerModel({
|
||||
name: 'LivechatButtonView',
|
||||
identifyingFields: ['publicLivechatGlobalOwner'],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({ widget: this.env.services.public_livechat_service.mountLivechatButton() });
|
||||
@@ -609,6 +608,7 @@ registerModel({
|
||||
compute: '_computeOpenChatDebounced',
|
||||
}),
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
identifying: true,
|
||||
inverse: 'livechatButtonView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -5,9 +5,9 @@ import { attr } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'LivechatOperator',
|
||||
identifyingFields: ['id'],
|
||||
fields: {
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -8,7 +8,6 @@ import { clear, insertAndReplace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'PublicLivechat',
|
||||
identifyingFields: ['publicLivechatGlobalOwner'],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({
|
||||
@@ -93,6 +92,7 @@ registerModel({
|
||||
}),
|
||||
legacyPublicLivechat: attr(),
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
identifying: true,
|
||||
inverse: 'publicLivechat',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -7,7 +7,6 @@ import { attr, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'PublicLivechatFeedbackView',
|
||||
identifyingFields: ['publicLivechatGlobalOwner'],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({
|
||||
@@ -35,6 +34,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
identifying: true,
|
||||
inverse: 'feedbackView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -10,7 +10,6 @@ import { get_cookie, Markup, set_cookie } from 'web.utils';
|
||||
|
||||
registerModel({
|
||||
name: 'PublicLivechatGlobal',
|
||||
identifyingFields: ['messaging'],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
// History tracking
|
||||
|
||||
@@ -9,7 +9,6 @@ import utils from 'web.utils';
|
||||
|
||||
registerModel({
|
||||
name: 'PublicLivechatGlobalNotificationHandler',
|
||||
identifyingFields: ['publicLivechatGlobalOwner'],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.env.services['bus_service'].addChannel(this.messaging.publicLivechatGlobal.publicLivechat.uuid);
|
||||
@@ -96,6 +95,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
identifying: true,
|
||||
inverse: 'notificationHandler',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'PublicLivechatMessage',
|
||||
identifyingFields: ['id'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -25,6 +24,7 @@ registerModel({
|
||||
}),
|
||||
data: attr(),
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -7,7 +7,6 @@ import { attr, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'PublicLivechatView',
|
||||
identifyingFields: ['publicLivechatWindowOwner'],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({
|
||||
@@ -20,6 +19,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
publicLivechatWindowOwner: one('PublicLivechatWindow', {
|
||||
identifying: true,
|
||||
inverse: 'publicLivechatView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -8,7 +8,6 @@ import { insertAndReplace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'PublicLivechatWindow',
|
||||
identifyingFields: ['livechatButtonViewOwner'],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({
|
||||
@@ -43,6 +42,7 @@ registerModel({
|
||||
default: null,
|
||||
}),
|
||||
livechatButtonViewOwner: one('LivechatButtonView', {
|
||||
identifying: true,
|
||||
inverse: 'chatWindow',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -250,23 +250,6 @@ function assertNameIsAvailableOnRecords(name, modelDefinition) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the provided function to the identifying fields of the model
|
||||
* specified by the `modelName`.
|
||||
*
|
||||
* @param {string} modelName The name of the model to which to apply the patch.
|
||||
* @param {function} patch The function to be applied on the identifying fields.
|
||||
*/
|
||||
export function patchIdentifyingFields(modelName, patch) {
|
||||
addContextToErrors(() => {
|
||||
if (!registry.has(modelName)) {
|
||||
throw new Error(`model must be registered before being patched.`);
|
||||
}
|
||||
assertIsFunction(patch);
|
||||
}, `Cannot patch identifyingFields on model "${modelName}": `);
|
||||
patch(registry.get(modelName).get('identifyingFields'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the lifecycle hooks of the model specified by the `modelName`.
|
||||
*
|
||||
@@ -343,7 +326,7 @@ export function patchRecordMethods(modelName, recordMethods) {
|
||||
/**
|
||||
* @param {Object} definition The JSON definition of the model to register.
|
||||
* @param {Object} [definition.fields]
|
||||
* @param {string[]} definition.identifyingFields
|
||||
* @param {string} [definition.identifyingMode='and']
|
||||
* @param {Object} [definition.lifecycleHooks]
|
||||
* @param {Object} [definition.modelGetters] Deprecated; use fields instead.
|
||||
* @param {Object} [definition.modelMethods]
|
||||
@@ -352,19 +335,19 @@ export function patchRecordMethods(modelName, recordMethods) {
|
||||
* @param {Object} [definition.recordGetters] Deprecated; use fields instead.
|
||||
* @param {Object} [definition.recordMethods]
|
||||
*/
|
||||
export function registerModel({ fields, identifyingFields, lifecycleHooks, modelGetters, modelMethods, name, onChanges, recordGetters, recordMethods }) {
|
||||
export function registerModel({ fields, identifyingMode = 'and', lifecycleHooks, modelGetters, modelMethods, name, onChanges, recordGetters, recordMethods }) {
|
||||
if (!name) {
|
||||
throw new Error("Model is lacking a name.");
|
||||
}
|
||||
if (!identifyingFields) {
|
||||
throw new Error(`Cannot register model "${name}": definition is lacking identifying fields.`);
|
||||
if (!identifyingMode) {
|
||||
throw new Error(`Cannot register model "${name}": definition is lacking identifying mode.`);
|
||||
}
|
||||
if (registry.has(name)) {
|
||||
throw new Error(`Cannot register model "${name}": model has already been registered.`);
|
||||
}
|
||||
registry.set(name, new Map([
|
||||
['name', name],
|
||||
['identifyingFields', identifyingFields],
|
||||
['identifyingMode', identifyingMode],
|
||||
['lifecycleHooks', new Map()],
|
||||
['modelMethods', new Map()],
|
||||
['modelGetters', new Map()],
|
||||
|
||||
@@ -18,6 +18,7 @@ export class ModelField {
|
||||
default: def,
|
||||
fieldName,
|
||||
fieldType,
|
||||
identifying = false,
|
||||
inverse,
|
||||
isCausal = false,
|
||||
readonly = false,
|
||||
@@ -52,6 +53,10 @@ export class ModelField {
|
||||
* 2. 'relation': fields that relate to some other records.
|
||||
*/
|
||||
this.fieldType = fieldType;
|
||||
/**
|
||||
* Determines whether this field is an identifying field for its model.
|
||||
*/
|
||||
this.identifying = identifying;
|
||||
/**
|
||||
* This prop only makes sense in a relational field. This contains
|
||||
* the name of the field name in the inverse relation. This may not
|
||||
|
||||
@@ -197,8 +197,7 @@ export class ModelManager {
|
||||
for (const model of Object.values(this.models)) {
|
||||
delete model.__fieldList;
|
||||
delete model.__fieldMap;
|
||||
delete model.__identifyingFields;
|
||||
delete model.__identifyingFieldsFlattened;
|
||||
delete model.__identifyingFieldNames;
|
||||
delete model.__records;
|
||||
delete model.__requiredFieldsList;
|
||||
delete model.fields;
|
||||
@@ -418,7 +417,7 @@ export class ModelManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds fields, methods, getters, and identifyingFields from the model
|
||||
* Adds fields, methods, getters, and identifyingMode from the model
|
||||
* definition to the model, then registers it in `this.models`.
|
||||
*
|
||||
* @private
|
||||
@@ -439,17 +438,7 @@ export class ModelManager {
|
||||
model.fields = {};
|
||||
// Contains all records. key is local id, while value is the record.
|
||||
model.__records = {};
|
||||
model.__identifyingFields = definition.get('identifyingFields');
|
||||
const identifyingFieldsFlattened = new Set();
|
||||
for (const identifyingElement of model.__identifyingFields) {
|
||||
const identifyingFields = typeof identifyingElement === 'string'
|
||||
? [identifyingElement]
|
||||
: identifyingElement;
|
||||
for (const identifyingField of identifyingFields) {
|
||||
identifyingFieldsFlattened.add(identifyingField);
|
||||
}
|
||||
}
|
||||
model.__identifyingFieldsFlattened = identifyingFieldsFlattened;
|
||||
model.identifyingMode = definition.get('identifyingMode');
|
||||
this._listenersObservingAllByModel.set(model, new Map());
|
||||
this.models[model.name] = model;
|
||||
}
|
||||
@@ -476,6 +465,7 @@ export class ModelManager {
|
||||
'compute',
|
||||
'default',
|
||||
'fieldType',
|
||||
'identifying',
|
||||
'readonly',
|
||||
'related',
|
||||
'required',
|
||||
@@ -492,6 +482,7 @@ export class ModelManager {
|
||||
'compute',
|
||||
'default',
|
||||
'fieldType',
|
||||
'identifying',
|
||||
'inverse',
|
||||
'isCausal',
|
||||
'readonly',
|
||||
@@ -579,6 +570,17 @@ export class ModelManager {
|
||||
*/
|
||||
_checkProcessedFieldsOnModels() {
|
||||
for (const model of Object.values(this.models)) {
|
||||
switch (model.identifyingMode) {
|
||||
case 'and':
|
||||
break;
|
||||
case 'xor':
|
||||
if (model.__identifyingFieldNames.size === 0) {
|
||||
throw new Error(`No identifying fields has been specified for 'xor' identifying mode on ${model}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported identifying mode "${model.identifyingMode}" on ${model}. Must be one of 'and' or 'xor'.`);
|
||||
}
|
||||
for (const field of model.__fieldList) {
|
||||
const fieldName = field.fieldName;
|
||||
if (!(['attribute', 'relation'].includes(field.fieldType))) {
|
||||
@@ -617,7 +619,7 @@ export class ModelManager {
|
||||
throw new Error(`${field} on ${model} has its inverse ${inverseField} on ${relatedModel} referring to an invalid model (model(${inverseField.to})).`);
|
||||
}
|
||||
}
|
||||
for (const identifyingField of model.__identifyingFieldsFlattened) {
|
||||
for (const identifyingField of model.__identifyingFieldNames) {
|
||||
const field = model.__fieldMap[identifyingField];
|
||||
if (!field) {
|
||||
throw new Error(`Identifying field "${identifyingField}" is not a field on ${model}.`);
|
||||
@@ -966,51 +968,81 @@ export class ModelManager {
|
||||
* @returns {string}
|
||||
*/
|
||||
_getRecordIndex(model, data) {
|
||||
return `${model.name}(${model.__identifyingFields.map(identifyingElement => {
|
||||
const fieldName = typeof identifyingElement === 'string'
|
||||
? identifyingElement
|
||||
: identifyingElement.reduce((fieldName, currentFieldName) => {
|
||||
const fieldValue = data[currentFieldName] !== undefined
|
||||
? data[currentFieldName]
|
||||
: model.__fieldMap[currentFieldName].default;
|
||||
const parts = [];
|
||||
switch (model.identifyingMode) {
|
||||
case 'and':
|
||||
for (const fieldName of model.__identifyingFieldNames) {
|
||||
const fieldValue = this._getValueFromDataOrFromDefault(model, fieldName, data);
|
||||
if (fieldValue === undefined) {
|
||||
return fieldName;
|
||||
throw new Error(`Identifying field "${fieldName}" is lacking a value on ${model} with 'and' identifying mode`);
|
||||
}
|
||||
parts.push(this._getRecordIndexFromField(model, fieldName, fieldValue));
|
||||
}
|
||||
break;
|
||||
case 'xor': {
|
||||
const [fieldName, fieldValue] = [...model.__identifyingFieldNames].reduce(([fieldName, fieldValue], currentFieldName) => {
|
||||
const currentFieldValue = this._getValueFromDataOrFromDefault(model, currentFieldName, data);
|
||||
if (currentFieldValue === undefined) {
|
||||
return [fieldName, fieldValue];
|
||||
}
|
||||
if (fieldName) {
|
||||
throw new Error(`Identifying element on ${model} with "or" value should have only one of the conditional values given in data. Currently have both ${fieldName} and ${currentFieldName}.`);
|
||||
throw new Error(`Identifying field on ${model} with 'xor' identifying mode should have only one of the conditional values given in data. Currently have both "${fieldName}" and "${currentFieldName}".`);
|
||||
}
|
||||
return currentFieldName;
|
||||
}, undefined);
|
||||
if (!fieldName) {
|
||||
throw new Error(`Identifying element "${identifyingElement}" on ${model} is lacking a value.`);
|
||||
return [currentFieldName, currentFieldValue];
|
||||
}, [undefined, undefined]);
|
||||
parts.push(this._getRecordIndexFromField(model, fieldName, fieldValue));
|
||||
break;
|
||||
}
|
||||
const fieldValue = data[fieldName] !== undefined
|
||||
? data[fieldName]
|
||||
: model.__fieldMap[fieldName].default;
|
||||
if (fieldValue === undefined) {
|
||||
throw new Error(`Identifying field "${fieldName}" on ${model} is lacking a value.`);
|
||||
}
|
||||
const relationTo = model.__fieldMap[fieldName].to;
|
||||
if (!relationTo) {
|
||||
return `${fieldName}: ${fieldValue}`;
|
||||
}
|
||||
const OtherModel = this.models[relationTo];
|
||||
if (fieldValue instanceof OtherModel) {
|
||||
return `${fieldName}: ${this._getRecordIndex(OtherModel, fieldValue)}`;
|
||||
}
|
||||
const fieldValue2 = model.__fieldMap[fieldName].convertToFieldCommandList(fieldValue)[0];
|
||||
if (!(fieldValue2 instanceof FieldCommand)) {
|
||||
throw new Error(`Identifying element "${model}/${fieldName}" is expecting a command for relational field.`);
|
||||
}
|
||||
if (!['link', 'insert', 'replace', 'insert-and-replace'].includes(fieldValue2._name)) {
|
||||
throw new Error(`Identifying element "${model}/${fieldName}" is expecting a command of type "insert-and-replace", "replace", "insert" or "link". "${fieldValue2._name}" was given instead.`);
|
||||
}
|
||||
const relationValue = fieldValue2._value;
|
||||
if (!relationValue) {
|
||||
throw new Error(`Identifying element "${model}/${fieldName}" is lacking a relation value.`);
|
||||
}
|
||||
return `${fieldName}: ${this._getRecordIndex(OtherModel, relationValue)}`;
|
||||
}).join(', ')})`;
|
||||
}
|
||||
return `${model.name}(${parts.join(', ')})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the part of the record index that comes from the given fieldName
|
||||
* with the given fieldValue.
|
||||
*
|
||||
* @param {Object} model
|
||||
* @param {string} fieldName
|
||||
* @param {any} fieldValue
|
||||
* @returns {string}
|
||||
*/
|
||||
_getRecordIndexFromField(model, fieldName, fieldValue) {
|
||||
const relationTo = model.__fieldMap[fieldName].to;
|
||||
if (!relationTo) {
|
||||
return `${fieldName}: ${fieldValue}`;
|
||||
}
|
||||
const OtherModel = this.models[relationTo];
|
||||
if (fieldValue instanceof OtherModel) {
|
||||
return `${fieldName}: ${this._getRecordIndex(OtherModel, fieldValue)}`;
|
||||
}
|
||||
const fieldValue2 = model.__fieldMap[fieldName].convertToFieldCommandList(fieldValue)[0];
|
||||
if (!(fieldValue2 instanceof FieldCommand)) {
|
||||
throw new Error(`Identifying element "${model}/${fieldName}" is expecting a command for relational field.`);
|
||||
}
|
||||
if (!['link', 'insert', 'replace', 'insert-and-replace'].includes(fieldValue2._name)) {
|
||||
throw new Error(`Identifying element "${model}/${fieldName}" is expecting a command of type "insert-and-replace", "replace", "insert" or "link". "${fieldValue2._name}" was given instead.`);
|
||||
}
|
||||
const relationValue = fieldValue2._value;
|
||||
if (!relationValue) {
|
||||
throw new Error(`Identifying element "${model}/${fieldName}" is lacking a relation value.`);
|
||||
}
|
||||
return `${fieldName}: ${this._getRecordIndex(OtherModel, relationValue)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the given fieldName from the provided data if it is
|
||||
* defined or from default value of this field otherwise.
|
||||
*
|
||||
* @param {Object} model
|
||||
* @param {string} fieldName
|
||||
* @param {Object} data
|
||||
* @returns {any}
|
||||
*/
|
||||
_getValueFromDataOrFromDefault(model, fieldName, data) {
|
||||
if (data[fieldName] !== undefined) {
|
||||
return data[fieldName];
|
||||
}
|
||||
return model.__fieldMap[fieldName].default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1051,7 +1083,7 @@ export class ModelManager {
|
||||
fieldName: `_inverse_${model}/${field.fieldName}`,
|
||||
// Allows the inverse of an identifying field to be
|
||||
// automatically generated.
|
||||
isCausal: model.__identifyingFieldsFlattened.has(field.fieldName),
|
||||
isCausal: field.identifying,
|
||||
},
|
||||
));
|
||||
return inverseField;
|
||||
@@ -1230,8 +1262,12 @@ export class ModelManager {
|
||||
model.__requiredFieldsList = model.__fieldList.filter(
|
||||
field => field.required
|
||||
);
|
||||
// Add field accessors.
|
||||
for (const fieldName of Object.keys(model.__fieldMap)) {
|
||||
model.__identifyingFieldNames = new Set();
|
||||
for (const [fieldName, field] of Object.entries(model.__fieldMap)) {
|
||||
if (field.identifying) {
|
||||
model.__identifyingFieldNames.add(fieldName);
|
||||
}
|
||||
// Add field accessors.
|
||||
Object.defineProperty(model.prototype, fieldName, {
|
||||
get: function getFieldValue() { // this is bound to record
|
||||
const field = model.__fieldMap[fieldName];
|
||||
@@ -1290,10 +1326,10 @@ export class ModelManager {
|
||||
// Always update identifying fields first because updating other relational field will
|
||||
// trigger update of inverse field, which will require this identifying fields to be set
|
||||
// beforehand to properly detect whether this is already linked or not on the inverse.
|
||||
if (model.__identifyingFieldsFlattened.has(a) && !model.__identifyingFieldsFlattened.has(b)) {
|
||||
if (model.__identifyingFieldNames.has(a) && !model.__identifyingFieldNames.has(b)) {
|
||||
return -1;
|
||||
}
|
||||
if (!model.__identifyingFieldsFlattened.has(a) && model.__identifyingFieldsFlattened.has(b)) {
|
||||
if (!model.__identifyingFieldNames.has(a) && model.__identifyingFieldNames.has(b)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -8,7 +8,6 @@ const { markup } = owl;
|
||||
|
||||
registerModel({
|
||||
name: 'Activity',
|
||||
identifyingFields: ['id'],
|
||||
modelMethods: {
|
||||
/**
|
||||
* @param {Object} data
|
||||
@@ -289,6 +288,7 @@ registerModel({
|
||||
}),
|
||||
icon: attr(),
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'ActivityBoxView',
|
||||
identifyingFields: ['chatter'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles click on activity box title.
|
||||
@@ -32,6 +31,7 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
chatter: one('Chatter', {
|
||||
identifying: true,
|
||||
inverse: 'activityBoxView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -7,7 +7,6 @@ import { OnChange } from '@mail/model/model_onchange';
|
||||
|
||||
registerModel({
|
||||
name: 'ActivityGroup',
|
||||
identifyingFields: ['irModel'],
|
||||
modelMethods: {
|
||||
convertData(data) {
|
||||
return {
|
||||
@@ -45,6 +44,7 @@ registerModel({
|
||||
}),
|
||||
domain: attr(),
|
||||
irModel: one('ir.model', {
|
||||
identifying: true,
|
||||
inverse: 'activityGroup',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -7,7 +7,6 @@ import session from 'web.session';
|
||||
|
||||
registerModel({
|
||||
name: 'ActivityGroupView',
|
||||
identifyingFields: ['activityMenuViewOwner', 'activityGroup'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
@@ -78,11 +77,13 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
activityGroup: one('ActivityGroup', {
|
||||
identifying: true,
|
||||
inverse: 'activityGroupViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
activityMenuViewOwner: one('ActivityMenuView', {
|
||||
identifying: true,
|
||||
inverse: 'activityGroupViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'ActivityMarkDonePopoverContentView',
|
||||
identifyingFields: ['popoverViewOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles blur on this feedback textarea.
|
||||
@@ -105,6 +104,7 @@ registerModel({
|
||||
default: '',
|
||||
}),
|
||||
popoverViewOwner: one('PopoverView', {
|
||||
identifying: true,
|
||||
inverse: 'activityMarkDonePopoverContentView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -8,7 +8,6 @@ import session from 'web.session';
|
||||
|
||||
registerModel({
|
||||
name: 'ActivityMenuView',
|
||||
identifyingFields: [],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.fetchData();
|
||||
|
||||
@@ -5,13 +5,13 @@ import { attr, many } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'ActivityType',
|
||||
identifyingFields: ['id'],
|
||||
fields: {
|
||||
activities: many('Activity', {
|
||||
inverse: 'type',
|
||||
}),
|
||||
displayName: attr(),
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -9,7 +9,6 @@ import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
name: 'ActivityView',
|
||||
identifyingFields: ['activityBoxView', 'activity'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles the click on a link inside the activity.
|
||||
@@ -169,11 +168,13 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
activity: one('Activity', {
|
||||
identifying: true,
|
||||
inverse: 'activityViews',
|
||||
required: true,
|
||||
readonly: true,
|
||||
}),
|
||||
activityBoxView: one('ActivityBoxView', {
|
||||
identifying: true,
|
||||
inverse: 'activityViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insert, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'Attachment',
|
||||
identifyingFields: ['id'],
|
||||
modelMethods: {
|
||||
/**
|
||||
* @static
|
||||
@@ -330,6 +329,7 @@ registerModel({
|
||||
}),
|
||||
filename: attr(),
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insertAndReplace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentBoxView',
|
||||
identifyingFields: ['chatter'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles click on the "add attachment" button.
|
||||
@@ -35,6 +34,7 @@ registerModel({
|
||||
readonly: true,
|
||||
}),
|
||||
chatter: one('Chatter', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentBoxView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentCard',
|
||||
identifyingFields: ['attachmentList', 'attachment'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Opens the attachment viewer when clicking on viewable attachment.
|
||||
@@ -49,6 +48,7 @@ registerModel({
|
||||
* Determines the attachment of this card.
|
||||
*/
|
||||
attachment: one('Attachment', {
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
@@ -60,6 +60,7 @@ registerModel({
|
||||
* States the attachmentList displaying this card.
|
||||
*/
|
||||
attachmentList: one('AttachmentList', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentCards',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -7,7 +7,6 @@ import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentDeleteConfirmView',
|
||||
identifyingFields: ['dialogOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Returns whether the given html element is inside this attachment delete confirm view.
|
||||
@@ -84,6 +83,7 @@ registerModel({
|
||||
}),
|
||||
component: attr(),
|
||||
dialogOwner: one('Dialog', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentDeleteConfirmView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -7,7 +7,6 @@ import { isEventHandled, markEventHandled } from '@mail/utils/utils';
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentImage',
|
||||
identifyingFields: ['attachmentList', 'attachment'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Called when clicking on download icon.
|
||||
@@ -115,6 +114,7 @@ registerModel({
|
||||
* Determines the attachment of this attachment image..
|
||||
*/
|
||||
attachment: one('Attachment', {
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
@@ -126,6 +126,7 @@ registerModel({
|
||||
* States the attachmentList displaying this attachment image.
|
||||
*/
|
||||
attachmentList: one('AttachmentList', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentImages',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { clear, insertAndReplace, replace } from '@mail/model/model_field_comman
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentList',
|
||||
identifyingFields: [['composerViewOwner', 'messageViewOwner', 'attachmentBoxViewOwner']],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
/**
|
||||
* Select the next attachment.
|
||||
@@ -137,6 +137,7 @@ registerModel({
|
||||
* Link with a AttachmentBoxView to handle attachments.
|
||||
*/
|
||||
attachmentBoxViewOwner: one('AttachmentBoxView', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentList',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -171,6 +172,7 @@ registerModel({
|
||||
* Link with a composer view to handle attachments.
|
||||
*/
|
||||
composerViewOwner: one('ComposerView', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentList',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -220,6 +222,7 @@ registerModel({
|
||||
* Link with a message view to handle attachments.
|
||||
*/
|
||||
messageViewOwner: one('MessageView', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentList',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insertAndReplace, replace } from '@mail/model/model_field_comman
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentViewer',
|
||||
identifyingFields: ['dialogOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Close the dialog with this attachment viewer.
|
||||
@@ -249,6 +248,7 @@ registerModel({
|
||||
* Determines the dialog displaying this attachment viewer.
|
||||
*/
|
||||
dialogOwner: one('Dialog', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentViewer',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { attr, one } from "@mail/model/model_field";
|
||||
|
||||
registerModel({
|
||||
name: "AttachmentViewerViewable",
|
||||
identifyingFields: [["attachmentOwner"]],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
download() {
|
||||
return this.attachmentOwner.download();
|
||||
@@ -94,6 +94,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
attachmentOwner: one("Attachment", {
|
||||
identifying: true,
|
||||
inverse: 'attachmentViewerViewable',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,12 +6,7 @@ import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'AutocompleteInputView',
|
||||
identifyingFields: [[
|
||||
'chatWindowOwnerAsNewMessage',
|
||||
'discussSidebarCategoryOwnerAsAddingItem',
|
||||
'discussViewOwnerAsMobileAddItemHeader',
|
||||
'messagingMenuOwnerAsMobileNewMessageInput',
|
||||
]],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {FocusEvent} ev
|
||||
@@ -92,6 +87,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
chatWindowOwnerAsNewMessage: one('ChatWindow', {
|
||||
identifying: true,
|
||||
inverse: 'newMessageAutocompleteInputView',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -101,10 +97,12 @@ registerModel({
|
||||
default: '',
|
||||
}),
|
||||
discussSidebarCategoryOwnerAsAddingItem: one('DiscussSidebarCategory', {
|
||||
identifying: true,
|
||||
inverse: 'addingItemAutocompleteInputView',
|
||||
readonly: true,
|
||||
}),
|
||||
discussViewOwnerAsMobileAddItemHeader: one('DiscussView', {
|
||||
identifying: true,
|
||||
inverse: 'mobileAddItemHeaderAutocompleteInputView',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -117,6 +115,7 @@ registerModel({
|
||||
default: false,
|
||||
}),
|
||||
messagingMenuOwnerAsMobileNewMessageInput: one('MessagingMenu', {
|
||||
identifying: true,
|
||||
inverse: 'mobileNewMessageAutocompleteInputView',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insertAndReplace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'CallActionListView',
|
||||
identifyingFields: ['callMainView'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
@@ -150,6 +149,7 @@ registerModel({
|
||||
default: '',
|
||||
}),
|
||||
callMainView: one('CallMainView', {
|
||||
identifying: true,
|
||||
inverse: 'callActionListView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -5,7 +5,6 @@ import { registerModel } from '@mail/model/model_core';
|
||||
|
||||
registerModel({
|
||||
name: 'CallDemoView',
|
||||
identifyingFields: ['welcomeView'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Stops recording user's microphone.
|
||||
@@ -174,6 +173,7 @@ registerModel({
|
||||
* States the welcome view containing this media preview.
|
||||
*/
|
||||
welcomeView: one('WelcomeView', {
|
||||
identifying: true,
|
||||
inverse: 'callDemoView',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -5,7 +5,6 @@ import { one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'CallInviteRequestPopup',
|
||||
identifyingFields: ['thread'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
@@ -35,6 +34,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
thread: one('Thread', {
|
||||
identifying: true,
|
||||
inverse: 'callInviteRequestPopup',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -8,7 +8,6 @@ import { isEventHandled, markEventHandled } from '@mail/utils/utils';
|
||||
|
||||
registerModel({
|
||||
name: 'CallMainView',
|
||||
identifyingFields: ['callView'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Finds a tile layout and dimensions that respects param0.aspectRatio while maximizing
|
||||
@@ -163,6 +162,7 @@ registerModel({
|
||||
readonly: true,
|
||||
}),
|
||||
callView: one('CallView', {
|
||||
identifying: true,
|
||||
inverse: 'callMainView',
|
||||
required: true,
|
||||
readonly: true,
|
||||
|
||||
@@ -6,14 +6,15 @@ import { one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'CallMainViewTile',
|
||||
identifyingFields: ['callMainViewOwner', 'channelMember'],
|
||||
fields: {
|
||||
callMainViewOwner: one('CallMainView', {
|
||||
identifying: true,
|
||||
inverse: 'mainTiles',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
channelMember: one('ChannelMember', {
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'CallOptionMenu',
|
||||
identifyingFields: ['callActionListView'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {Event} ev
|
||||
@@ -67,6 +66,7 @@ registerModel({
|
||||
*/
|
||||
component: attr(),
|
||||
callActionListView: one('CallActionListView', {
|
||||
identifying: true,
|
||||
inverse: 'callOptionMenu',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
name: 'CallParticipantCard',
|
||||
identifyingFields: [['sidebarViewTileOwner', 'mainViewTileOwner']],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {Event} ev
|
||||
@@ -168,6 +168,7 @@ registerModel({
|
||||
compute: '_computeInboundConnectionTypeText',
|
||||
}),
|
||||
mainViewTileOwner: one('CallMainViewTile', {
|
||||
identifying: true,
|
||||
inverse: 'participantCard',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -203,6 +204,7 @@ registerModel({
|
||||
inverse: 'callParticipantCards',
|
||||
}),
|
||||
sidebarViewTileOwner: one('CallSidebarViewTile', {
|
||||
identifying: true,
|
||||
inverse: 'participantCard',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'CallParticipantVideoView',
|
||||
identifyingFields: ['callParticipantCardOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Plays the video as some browsers may not support or block autoplay.
|
||||
@@ -38,6 +37,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
callParticipantCardOwner: one('CallParticipantCard', {
|
||||
identifying: true,
|
||||
inverse: 'callParticipantVideoView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -7,7 +7,6 @@ import { attr, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'CallSettingsMenu',
|
||||
identifyingFields: ['userSetting'],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
browser.addEventListener('keydown', this._onKeyDown);
|
||||
@@ -83,6 +82,7 @@ registerModel({
|
||||
default: false,
|
||||
}),
|
||||
userSetting: one('UserSetting', {
|
||||
identifying: true,
|
||||
inverse: 'callSettingsMenu',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'CallSidebarView',
|
||||
identifyingFields: ['callView'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -18,6 +17,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
callView: one('CallView', {
|
||||
identifying: true,
|
||||
inverse: 'callSidebarView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,14 +6,15 @@ import { one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'CallSidebarViewTile',
|
||||
identifyingFields: ['callSidebarViewOwner', 'channelMember'],
|
||||
fields: {
|
||||
callSidebarViewOwner: one('CallSidebarView', {
|
||||
identifying: true,
|
||||
inverse: 'sidebarTiles',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
channelMember: one('ChannelMember', {
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -8,7 +8,6 @@ import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
name: 'CallSystrayMenu',
|
||||
identifyingFields: ['rtc'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -30,6 +29,7 @@ registerModel({
|
||||
default: '',
|
||||
}),
|
||||
rtc: one('Rtc', {
|
||||
identifying: true,
|
||||
inverse: 'callSystrayMenu',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -9,7 +9,6 @@ import { clear, insertAndReplace, replace } from '@mail/model/model_field_comman
|
||||
|
||||
registerModel({
|
||||
name: 'CallView',
|
||||
identifyingFields: ['threadView'],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
browser.addEventListener('fullscreenchange', this._onFullScreenChange);
|
||||
@@ -229,6 +228,7 @@ registerModel({
|
||||
* ThreadView on which the call view is attached.
|
||||
*/
|
||||
threadView: one('ThreadView', {
|
||||
identifying: true,
|
||||
inverse: 'callView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -7,7 +7,6 @@ import { cleanSearchTerm } from '@mail/utils/utils';
|
||||
|
||||
registerModel({
|
||||
name: 'CannedResponse',
|
||||
identifyingFields: ['id'],
|
||||
modelMethods: {
|
||||
/**
|
||||
* Fetches canned responses matching the given search term to extend the
|
||||
@@ -71,6 +70,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'Channel',
|
||||
identifyingFields: ['id'],
|
||||
recordMethods: {
|
||||
async fetchChannelMembers() {
|
||||
const channelData = await this.messaging.rpc({
|
||||
@@ -92,6 +91,7 @@ registerModel({
|
||||
}),
|
||||
channel_type: attr(),
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -7,7 +7,6 @@ import { cleanSearchTerm } from '@mail/utils/utils';
|
||||
|
||||
registerModel({
|
||||
name: 'ChannelCommand',
|
||||
identifyingFields: ['name'],
|
||||
modelMethods: {
|
||||
/**
|
||||
* Fetches channel commands matching the given search term to extend the
|
||||
@@ -128,6 +127,7 @@ registerModel({
|
||||
* The keyword to use a specific command.
|
||||
*/
|
||||
name: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -9,7 +9,7 @@ import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
name: 'ChannelInvitationForm',
|
||||
identifyingFields: [['chatWindow', 'popoverViewOwner']],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles click on the "copy" button.
|
||||
@@ -236,6 +236,7 @@ registerModel({
|
||||
compute: '_computeAccessRestrictedToGroupText',
|
||||
}),
|
||||
chatWindow: one('ChatWindow', {
|
||||
identifying: true,
|
||||
inverse: 'channelInvitationForm',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -271,6 +272,7 @@ registerModel({
|
||||
* If set, this channel invitation form is content of related popover view.
|
||||
*/
|
||||
popoverViewOwner: one('PopoverView', {
|
||||
identifying: true,
|
||||
inverse: 'channelInvitationForm',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insertAndReplace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'ChannelInvitationFormSelectablePartnerView',
|
||||
identifyingFields: ['channelInvitationFormOwner', 'partner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -18,11 +17,13 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
channelInvitationFormOwner: one('ChannelInvitationForm', {
|
||||
identifying: true,
|
||||
inverse: 'selectablePartnerViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
partner: one('Partner', {
|
||||
identifying: true,
|
||||
inverse: 'channelInvitationFormSelectablePartnerViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -5,14 +5,15 @@ import { one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'ChannelInvitationFormSelectedPartnerView',
|
||||
identifyingFields: ['channelInvitationFormOwner', 'partner'],
|
||||
fields: {
|
||||
channelInvitationFormOwner: one('ChannelInvitationForm', {
|
||||
identifying: true,
|
||||
inverse: 'selectedPartnerViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
partner: one('Partner', {
|
||||
identifying: true,
|
||||
inverse: 'channelInvitationFormSelectedPartnerViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'ChannelMember',
|
||||
identifyingFields: ['id'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -81,6 +80,7 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -8,7 +8,7 @@ import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
name: 'ChannelMemberListCategoryView',
|
||||
identifyingFields: [['channelMemberListViewOwnerAsOffline', 'channelMemberListViewOwnerAsOnline']],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -77,10 +77,12 @@ registerModel({
|
||||
required: true,
|
||||
}),
|
||||
channelMemberListViewOwnerAsOffline: one('ChannelMemberListView', {
|
||||
identifying: true,
|
||||
inverse: 'offlineCategoryView',
|
||||
readonly: true,
|
||||
}),
|
||||
channelMemberListViewOwnerAsOnline: one('ChannelMemberListView', {
|
||||
identifying: true,
|
||||
inverse: 'onlineCategoryView',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,7 @@ import { clear, insertAndReplace, replace } from '@mail/model/model_field_comman
|
||||
|
||||
registerModel({
|
||||
name: 'ChannelMemberListView',
|
||||
identifyingFields: [['chatWindowOwner', 'threadViewOwner']],
|
||||
identifyingMode: 'xor',
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.channel.fetchChannelMembers();
|
||||
@@ -59,6 +59,7 @@ registerModel({
|
||||
readonly: true,
|
||||
}),
|
||||
chatWindowOwner: one('ChatWindow', {
|
||||
identifying: true,
|
||||
inverse: 'channelMemberListView',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -73,6 +74,7 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
threadViewOwner: one('ThreadView', {
|
||||
identifying: true,
|
||||
inverse: 'channelMemberListView',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear, insertAndReplace } from '@mail/model/model_field_command';
|
||||
import { isEventHandled } from '@mail/utils/utils'
|
||||
import { isEventHandled } from '@mail/utils/utils';
|
||||
|
||||
registerModel({
|
||||
name: 'ChannelMemberView',
|
||||
identifyingFields: ['channelMemberListCategoryViewOwner', 'channelMember'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles click on channel member in the member list of this channel.
|
||||
@@ -47,11 +46,13 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
channelMemberListCategoryViewOwner: one('ChannelMemberListCategoryView', {
|
||||
identifying: true,
|
||||
inverse: 'channelMemberViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
channelMember: one('ChannelMember', {
|
||||
identifying: true,
|
||||
inverse: 'channelMemberViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { isEventHandled, markEventHandled } from '@mail/utils/utils';
|
||||
|
||||
registerModel({
|
||||
name: 'ChatWindow',
|
||||
identifyingFields: ['manager', ['thread', 'managerAsNewMessage']],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
/**
|
||||
* Close this chat window.
|
||||
@@ -658,6 +658,7 @@ registerModel({
|
||||
readonly: true,
|
||||
}),
|
||||
managerAsNewMessage: one('ChatWindowManager', {
|
||||
identifying: true,
|
||||
inverse: 'newMessageChatWindow',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -681,6 +682,7 @@ registerModel({
|
||||
* If no `Thread` is linked, `this` is considered "new message".
|
||||
*/
|
||||
thread: one('Thread', {
|
||||
identifying: true,
|
||||
inverse: 'chatWindow',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { isEventHandled } from '@mail/utils/utils';
|
||||
|
||||
registerModel({
|
||||
name: 'ChatWindowHeaderView',
|
||||
identifyingFields: ['chatWindowOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
@@ -28,6 +27,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
chatWindowOwner: one('ChatWindow', {
|
||||
identifying: true,
|
||||
inverse: 'chatWindowHeaderView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -44,7 +44,6 @@ const BASE_VISUAL = {
|
||||
|
||||
registerModel({
|
||||
name: 'ChatWindowManager',
|
||||
identifyingFields: ['messaging'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Close all chat windows.
|
||||
|
||||
@@ -23,7 +23,6 @@ const getMessageNextTemporaryId = (function () {
|
||||
|
||||
registerModel({
|
||||
name: 'Chatter',
|
||||
identifyingFields: ['id'],
|
||||
recordMethods: {
|
||||
focus() {
|
||||
if (this.composerView) {
|
||||
@@ -439,6 +438,7 @@ registerModel({
|
||||
* of this record.
|
||||
*/
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -7,7 +7,6 @@ import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
name: 'ChatterTopbar',
|
||||
identifyingFields: ['chatter'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -41,6 +40,7 @@ registerModel({
|
||||
compute: '_computeAttachmentButtonText',
|
||||
}),
|
||||
chatter: one('Chatter', {
|
||||
identifying: true,
|
||||
inverse: 'topbar',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -9,7 +9,6 @@ import { OnChange } from '@mail/model/model_onchange';
|
||||
*/
|
||||
registerModel({
|
||||
name: 'Clock',
|
||||
identifyingFields: ['frequency'],
|
||||
lifecycleHooks: {
|
||||
_willDelete() {
|
||||
this.messaging.browser.clearInterval(this.tickInterval);
|
||||
@@ -62,6 +61,7 @@ registerModel({
|
||||
* must be recomputed.
|
||||
*/
|
||||
frequency: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -8,9 +8,10 @@ import { one } from '@mail/model/model_field';
|
||||
*/
|
||||
registerModel({
|
||||
name: 'ClockWatcher',
|
||||
identifyingFields: [['activityViewOwner', 'messageViewOwner']],
|
||||
identifyingMode: 'xor',
|
||||
fields: {
|
||||
activityViewOwner: one('ActivityView', {
|
||||
identifying: true,
|
||||
inverse: 'clockWatcher',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -19,6 +20,7 @@ registerModel({
|
||||
required: true,
|
||||
}),
|
||||
messageViewOwner: one('MessageView', {
|
||||
identifying: true,
|
||||
inverse: 'clockWatcher',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -7,7 +7,7 @@ import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
name: 'Composer',
|
||||
identifyingFields: [['thread', 'messageViewInEditing']],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -185,6 +185,7 @@ registerModel({
|
||||
compute: '_computeMentionedPartners',
|
||||
}),
|
||||
messageViewInEditing: one('MessageView', {
|
||||
identifying: true,
|
||||
inverse: 'composerForEditing',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -218,6 +219,7 @@ registerModel({
|
||||
* States the thread which this composer represents the state (if any).
|
||||
*/
|
||||
thread: one('Thread', {
|
||||
identifying: true,
|
||||
inverse: 'composer',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -5,13 +5,15 @@ import { many, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'ComposerSuggestable',
|
||||
identifyingFields: [['cannedResponse', 'channelCommand', 'partner', 'thread']],
|
||||
identifyingMode: 'xor',
|
||||
fields: {
|
||||
cannedResponse: one('CannedResponse', {
|
||||
identifying: true,
|
||||
inverse: 'suggestable',
|
||||
readonly: true,
|
||||
}),
|
||||
channelCommand: one('ChannelCommand', {
|
||||
identifying: true,
|
||||
inverse: 'suggestable',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -24,10 +26,12 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
partner: one('Partner', {
|
||||
identifying: true,
|
||||
inverse: 'suggestable',
|
||||
readonly: true,
|
||||
}),
|
||||
thread: one('Thread', {
|
||||
identifying: true,
|
||||
inverse: 'suggestable',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insertAndReplace, replace } from '@mail/model/model_field_comman
|
||||
|
||||
registerModel({
|
||||
name: 'ComposerSuggestedRecipientListView',
|
||||
identifyingFields: ['composerViewOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
@@ -63,6 +62,7 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
composerViewOwner: one('ComposerView', {
|
||||
identifying: true,
|
||||
inverse: 'composerSuggestedRecipientListView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -5,14 +5,15 @@ import { one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'ComposerSuggestedRecipientView',
|
||||
identifyingFields: ['composerSuggestedRecipientListViewOwner', 'suggestedRecipientInfo'],
|
||||
fields: {
|
||||
composerSuggestedRecipientListViewOwner: one('ComposerSuggestedRecipientListView', {
|
||||
identifying: true,
|
||||
inverse: 'composerSuggestedRecipientViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
suggestedRecipientInfo: one('SuggestedRecipientInfo', {
|
||||
identifying: true,
|
||||
inverse: 'composerSuggestedRecipientViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'ComposerSuggestionListView',
|
||||
identifyingFields: ['composerViewOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Sets the first suggestion as active. Main and extra records are
|
||||
@@ -120,6 +119,7 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
composerViewOwner: one('ComposerView', {
|
||||
identifying: true,
|
||||
inverse: 'composerSuggestionListView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
+2
-1
@@ -11,9 +11,9 @@ import { insertAndReplace } from '@mail/model/model_field_command';
|
||||
*/
|
||||
registerModel({
|
||||
name: 'ComposerSuggestionListViewExtraComposerSuggestionViewItem',
|
||||
identifyingFields: ['composerSuggestionListViewOwner', 'suggestable'],
|
||||
fields: {
|
||||
composerSuggestionListViewOwner: one('ComposerSuggestionListView', {
|
||||
identifying: true,
|
||||
inverse: 'composerSuggestionListViewExtraComposerSuggestionViewItems',
|
||||
readonly: true,
|
||||
required: true,
|
||||
@@ -26,6 +26,7 @@ registerModel({
|
||||
required: true,
|
||||
}),
|
||||
suggestable: one('ComposerSuggestable', {
|
||||
identifying: true,
|
||||
inverse: 'composerSuggestionListViewExtraComposerSuggestionViewItems',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
+2
-1
@@ -11,9 +11,9 @@ import { insertAndReplace } from '@mail/model/model_field_command';
|
||||
*/
|
||||
registerModel({
|
||||
name: 'ComposerSuggestionListViewMainComposerSuggestionViewItem',
|
||||
identifyingFields: ['composerSuggestionListViewOwner', 'suggestable'],
|
||||
fields: {
|
||||
composerSuggestionListViewOwner: one('ComposerSuggestionListView', {
|
||||
identifying: true,
|
||||
inverse: 'composerSuggestionListViewMainComposerSuggestionViewItems',
|
||||
readonly: true,
|
||||
required: true,
|
||||
@@ -26,6 +26,7 @@ registerModel({
|
||||
required: true,
|
||||
}),
|
||||
suggestable: one('ComposerSuggestable', {
|
||||
identifying: true,
|
||||
inverse: 'composerSuggestionListViewMainComposerSuggestionViewItems',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { sprintf } from '@web/core/utils/strings';
|
||||
*/
|
||||
registerModel({
|
||||
name: 'ComposerSuggestionView',
|
||||
identifyingFields: [['composerSuggestionListViewExtraComposerSuggestionViewItemOwner', 'composerSuggestionListViewMainComposerSuggestionViewItemOwner']],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {Event} ev
|
||||
@@ -115,10 +115,12 @@ registerModel({
|
||||
inverse: 'activeSuggestionView',
|
||||
}),
|
||||
composerSuggestionListViewExtraComposerSuggestionViewItemOwner: one('ComposerSuggestionListViewExtraComposerSuggestionViewItem', {
|
||||
identifying: true,
|
||||
inverse: 'composerSuggestionView',
|
||||
readonly: true,
|
||||
}),
|
||||
composerSuggestionListViewMainComposerSuggestionViewItemOwner: one('ComposerSuggestionListViewMainComposerSuggestionViewItem', {
|
||||
identifying: true,
|
||||
inverse: 'composerSuggestionView',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -12,7 +12,7 @@ import { url } from '@web/core/utils/urls';
|
||||
|
||||
registerModel({
|
||||
name: 'ComposerView',
|
||||
identifyingFields: [['threadView', 'messageViewInEditing', 'chatter']],
|
||||
identifyingMode: 'xor',
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
document.addEventListener('click', this.onClickCaptureGlobal, true);
|
||||
@@ -1377,6 +1377,7 @@ registerModel({
|
||||
* States the chatter which this composer allows editing (if any).
|
||||
*/
|
||||
chatter: one('Chatter', {
|
||||
identifying: true,
|
||||
inverse: 'composerView',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -1542,6 +1543,7 @@ registerModel({
|
||||
* States the message view on which this composer allows editing (if any).
|
||||
*/
|
||||
messageViewInEditing: one('MessageView', {
|
||||
identifying: true,
|
||||
inverse: 'composerViewInEditing',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -1609,6 +1611,7 @@ registerModel({
|
||||
* States the thread view on which this composer allows editing (if any).
|
||||
*/
|
||||
threadView: one('ThreadView', {
|
||||
identifying: true,
|
||||
inverse: 'composerView',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'Country',
|
||||
identifyingFields: ['id'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -25,6 +24,7 @@ registerModel({
|
||||
compute: '_computeFlagUrl',
|
||||
}),
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insertAndReplace, replace } from '@mail/model/model_field_comman
|
||||
|
||||
registerModel({
|
||||
name: 'DeleteMessageConfirmView',
|
||||
identifyingFields: ['dialogOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Returns whether the given html element is inside this delete message confirm view.
|
||||
@@ -44,6 +43,7 @@ registerModel({
|
||||
fields: {
|
||||
component: attr(),
|
||||
dialogOwner: one('Dialog', {
|
||||
identifying: true,
|
||||
inverse: 'deleteMessageConfirmView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -8,7 +8,6 @@ import { SIZES } from '@web/core/ui/ui_service';
|
||||
|
||||
registerModel({
|
||||
name: 'Device',
|
||||
identifyingFields: ['messaging'],
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this._refresh();
|
||||
|
||||
@@ -6,13 +6,7 @@ import { clear, insertAndReplace, replace } from '@mail/model/model_field_comman
|
||||
|
||||
registerModel({
|
||||
name: 'Dialog',
|
||||
identifyingFields: [[
|
||||
'attachmentCardOwnerAsAttachmentDeleteConfirm',
|
||||
'attachmentImageOwnerAsAttachmentDeleteConfirm',
|
||||
'attachmentListOwnerAsAttachmentView',
|
||||
'followerOwnerAsSubtypeList',
|
||||
'messageActionListOwnerAsDeleteConfirm',
|
||||
]],
|
||||
identifyingMode: 'xor',
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
document.addEventListener('click', this._onClickGlobal, true);
|
||||
@@ -188,6 +182,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
attachmentCardOwnerAsAttachmentDeleteConfirm: one('AttachmentCard', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentDeleteConfirmDialog',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -197,10 +192,12 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
attachmentImageOwnerAsAttachmentDeleteConfirm: one('AttachmentImage', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentDeleteConfirmDialog',
|
||||
readonly: true,
|
||||
}),
|
||||
attachmentListOwnerAsAttachmentView: one('AttachmentList', {
|
||||
identifying: true,
|
||||
inverse: 'attachmentListViewDialog',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -225,6 +222,7 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
followerOwnerAsSubtypeList: one('Follower', {
|
||||
identifying: true,
|
||||
inverse: 'followerSubtypeListDialog',
|
||||
readonly: true,
|
||||
}),
|
||||
@@ -243,6 +241,7 @@ registerModel({
|
||||
readonly: true,
|
||||
}),
|
||||
messageActionListOwnerAsDeleteConfirm: one('MessageActionList', {
|
||||
identifying: true,
|
||||
inverse: 'deleteConfirmDialog',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -5,7 +5,6 @@ import { many } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'DialogManager',
|
||||
identifyingFields: ['messaging'],
|
||||
fields: {
|
||||
// FIXME: dependent on implementation that uses insert order in relations!!
|
||||
dialogs: many('Dialog', {
|
||||
|
||||
@@ -7,7 +7,6 @@ import { escape, sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
name: 'Discuss',
|
||||
identifyingFields: ['messaging'],
|
||||
recordMethods: {
|
||||
clearIsAddingItem() {
|
||||
this.update({
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insertAndReplace, replace } from '@mail/model/model_field_comman
|
||||
|
||||
registerModel({
|
||||
name: 'DiscussPublicView',
|
||||
identifyingFields: ['messaging'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Creates and displays the thread view and clears the welcome view.
|
||||
|
||||
@@ -7,7 +7,7 @@ import { OnChange } from '@mail/model/model_onchange';
|
||||
|
||||
registerModel({
|
||||
name: 'DiscussSidebarCategory',
|
||||
identifyingFields: [['discussAsChannel', 'discussAsChat']],
|
||||
identifyingMode: 'xor',
|
||||
modelMethods: {
|
||||
/**
|
||||
* Performs the `set_res_users_settings` RPC on `res.users.settings`.
|
||||
@@ -377,10 +377,12 @@ registerModel({
|
||||
sum: 'categoryItems.categoryCounterContribution',
|
||||
}),
|
||||
discussAsChannel: one('Discuss', {
|
||||
identifying: true,
|
||||
inverse: 'categoryChannel',
|
||||
readonly: true,
|
||||
}),
|
||||
discussAsChat: one('Discuss', {
|
||||
identifying: true,
|
||||
inverse: 'categoryChat',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -8,7 +8,6 @@ import Dialog from 'web.Dialog';
|
||||
|
||||
registerModel({
|
||||
name: 'DiscussSidebarCategoryItem',
|
||||
identifyingFields: ['category', 'thread'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -226,6 +225,7 @@ registerModel({
|
||||
* Determines the discuss sidebar category displaying this item.
|
||||
*/
|
||||
category: one('DiscussSidebarCategory', {
|
||||
identifying: true,
|
||||
inverse: 'categoryItems',
|
||||
readonly: true,
|
||||
required: true,
|
||||
@@ -288,6 +288,7 @@ registerModel({
|
||||
* The related thread.
|
||||
*/
|
||||
thread: one('Thread', {
|
||||
identifying: true,
|
||||
inverse: 'discussSidebarCategoryItem',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { clear, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'DiscussSidebarMailboxView',
|
||||
identifyingFields: [['discussViewOwnerAsHistory', 'discussViewOwnerAsInbox', 'discussViewOwnerAsStarred']],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -27,14 +27,17 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
discussViewOwnerAsHistory: one('DiscussView', {
|
||||
identifying: true,
|
||||
inverse: 'historyView',
|
||||
readonly: true,
|
||||
}),
|
||||
discussViewOwnerAsInbox: one('DiscussView', {
|
||||
identifying: true,
|
||||
inverse: 'inboxView',
|
||||
readonly: true,
|
||||
}),
|
||||
discussViewOwnerAsStarred: one('DiscussView', {
|
||||
identifying: true,
|
||||
inverse: 'starredView',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -7,7 +7,6 @@ import { OnChange } from '@mail/model/model_onchange';
|
||||
|
||||
registerModel({
|
||||
name: 'DiscussView',
|
||||
identifyingFields: ['discuss'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles click on the mobile "new channel" button.
|
||||
@@ -136,6 +135,7 @@ registerModel({
|
||||
*/
|
||||
actionId: attr(),
|
||||
discuss: one('Discuss', {
|
||||
identifying: true,
|
||||
inverse: 'discussView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { decrement, increment } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'DropZoneView',
|
||||
identifyingFields: [['chatterOwner', 'composerViewOwner']],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
/**
|
||||
* Shows a visual drop effect when dragging inside the dropzone.
|
||||
@@ -94,10 +94,12 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
chatterOwner: one('Chatter', {
|
||||
identifying: true,
|
||||
inverse: 'dropZoneView',
|
||||
readonly: true,
|
||||
}),
|
||||
composerViewOwner: one('ComposerView', {
|
||||
identifying: true,
|
||||
inverse: 'dropZoneView',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'Emoji',
|
||||
identifyingFields: ['unicode'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -38,6 +37,7 @@ registerModel({
|
||||
readonly: true,
|
||||
}),
|
||||
unicode: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -5,12 +5,12 @@ import { attr, many } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiCategory',
|
||||
identifyingFields: ['categoryName'],
|
||||
fields: {
|
||||
allEmojis: many('Emoji', {
|
||||
inverse: 'emojiCategories',
|
||||
}),
|
||||
categoryName: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiCategoryBarView',
|
||||
identifyingFields: ['emojiPickerViewOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -58,6 +57,7 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
emojiPickerViewOwner: one('EmojiPickerView', {
|
||||
identifying: true,
|
||||
inverse: 'emojiCategoryBarView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,9 +6,8 @@ import { replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiCategoryView',
|
||||
identifyingFields: ['emojiCategoryBarViewOwner', 'emojiCategory'],
|
||||
recordMethods: {
|
||||
/**
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClick() {
|
||||
@@ -29,11 +28,13 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
emojiCategory: one('EmojiCategory', {
|
||||
identifying: true,
|
||||
inverse: 'emojiCategoryViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
emojiCategoryBarViewOwner: one('EmojiCategoryBarView', {
|
||||
identifying: true,
|
||||
inverse: 'emojiCategoryViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insertAndReplace, replace } from '@mail/model/model_field_comman
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiGridView',
|
||||
identifyingFields: ['emojiPickerViewOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -25,6 +24,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
emojiPickerViewOwner: one('EmojiPickerView', {
|
||||
identifying: true,
|
||||
inverse: 'emojiGridView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { insertAndReplace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiPickerView',
|
||||
identifyingFields: ['popoverViewOwner'],
|
||||
fields: {
|
||||
emojiCategoryBarView: one('EmojiCategoryBarView', {
|
||||
default: insertAndReplace(),
|
||||
@@ -23,6 +22,7 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
popoverViewOwner: one('PopoverView', {
|
||||
identifying: true,
|
||||
inverse: 'emojiPickerView',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { insertAndReplace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiRegistry',
|
||||
identifyingFields: ['messaging'],
|
||||
fields: {
|
||||
allEmojis: many('Emoji', {
|
||||
default: insertAndReplace([
|
||||
|
||||
@@ -5,7 +5,6 @@ import { attr, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiView',
|
||||
identifyingFields: ['emojiGridView', 'emoji'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
@@ -35,11 +34,13 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
emoji: one('Emoji', {
|
||||
identifying: true,
|
||||
inverse: 'emojiViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
emojiGridView: one('EmojiGridView', {
|
||||
identifying: true,
|
||||
inverse: 'emojiViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
@@ -16,7 +16,7 @@ const getAttachmentNextTemporaryId = (function () {
|
||||
|
||||
registerModel({
|
||||
name: 'FileUploader',
|
||||
identifyingFields: [['activityView', 'attachmentBoxView', 'chatterOwner', 'composerView']],
|
||||
identifyingMode: 'xor',
|
||||
recordMethods: {
|
||||
openBrowserFileUploader() {
|
||||
this.fileInput.click();
|
||||
@@ -188,18 +188,22 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
activityView: one('ActivityView', {
|
||||
identifying: true,
|
||||
inverse: 'fileUploader',
|
||||
readonly: true,
|
||||
}),
|
||||
attachmentBoxView: one('AttachmentBoxView', {
|
||||
identifying: true,
|
||||
inverse: 'fileUploader',
|
||||
readonly: true,
|
||||
}),
|
||||
chatterOwner: one('Chatter', {
|
||||
identifying: true,
|
||||
inverse: 'fileUploader',
|
||||
readonly: true,
|
||||
}),
|
||||
composerView: one('ComposerView', {
|
||||
identifying: true,
|
||||
inverse: 'fileUploader',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'FollowButtonView',
|
||||
identifyingFields: [['chatterOwner']],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
@@ -64,6 +63,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
chatterOwner: one('Chatter', {
|
||||
identifying: true,
|
||||
inverse: 'followButtonView',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insert, insertAndReplace, link, replace, unlink } from '@mail/mo
|
||||
|
||||
registerModel({
|
||||
name: 'Follower',
|
||||
identifyingFields: ['id'],
|
||||
modelMethods: {
|
||||
/**
|
||||
* @param {Object} data
|
||||
@@ -184,6 +183,7 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'FollowerListMenuView',
|
||||
identifyingFields: [['chatterOwner']],
|
||||
recordMethods: {
|
||||
hide() {
|
||||
this.update({ isDropdownOpen: false });
|
||||
@@ -48,6 +47,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
chatterOwner: one('Chatter', {
|
||||
identifying: true,
|
||||
inverse: 'followerListMenuView',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -5,7 +5,6 @@ import { attr, many } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'FollowerSubtype',
|
||||
identifyingFields: ['id'],
|
||||
modelMethods: {
|
||||
/**
|
||||
* @param {Object} data
|
||||
@@ -43,6 +42,7 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
id: attr({
|
||||
identifying: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clear, insertAndReplace, replace } from '@mail/model/model_field_comman
|
||||
|
||||
registerModel({
|
||||
name: 'FollowerSubtypeList',
|
||||
identifyingFields: ['dialogOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Returns whether the given html element is inside this follower subtype list.
|
||||
@@ -68,6 +67,7 @@ registerModel({
|
||||
* States the dialog displaying this follower subtype list.
|
||||
*/
|
||||
dialogOwner: one('Dialog', {
|
||||
identifying: true,
|
||||
inverse: 'followerSubtypeList',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
|
||||
@@ -5,7 +5,6 @@ import { one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'FollowerSubtypeView',
|
||||
identifyingFields: ['followerSubtypeListOwner', 'subtype'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Called when clicking on cancel button.
|
||||
@@ -25,11 +24,13 @@ registerModel({
|
||||
related: 'followerSubtypeListOwner.follower',
|
||||
}),
|
||||
followerSubtypeListOwner: one('FollowerSubtypeList', {
|
||||
identifying: true,
|
||||
inverse: 'followerSubtypeViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
subtype: one('FollowerSubtype', {
|
||||
identifying: true,
|
||||
inverse: 'followerSubtypeViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user