Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][MIG] web_m2x_options: mru #17

Open
wants to merge 1 commit into
base: 16.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions web_m2x_options/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ in the field's options dict

Deactivates the color picker on many2many_tags buttons to do nothing (ignored if open is set)

``search_mru`` *boolean* (Default: ``False``)

Allows to display to the user the 5 lasts records he selected for a given field. This list will be displayed
when the many2one field is focused and when the user didn't type anything to filter on. The values are stored in
the local storage of the browser and updated after a successful saved of the form. This features only works on form
views at the moment.

ir.config_parameter options
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -122,6 +129,15 @@ If you disable one option, you can enable it for particular field by setting "cr

Number of displayed lines on all One2many fields

``web_m2x_options.search_mru`` *boolean* (Default: default value is ``False``)

Enable MRU for all many2one fields (form view only).

``web_m2x_options.search_mru_max_length`` *int*

Changes the length of the records to store and show with MRU feature


To add these parameters go to Configuration -> Technical -> Parameters -> System Parameters and add new parameters like:

- web_m2x_options.create: False
Expand All @@ -130,6 +146,7 @@ To add these parameters go to Configuration -> Technical -> Parameters -> System
- web_m2x_options.limit: 10
- web_m2x_options.search_more: True
- web_m2x_options.field_limit_entries: 5
- web_m2x_options.search_mru: True


Example
Expand Down
7 changes: 6 additions & 1 deletion web_m2x_options/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
"website": "https://github.com/OCA/web",
"license": "AGPL-3",
"depends": ["web"],
"assets": {"web.assets_backend": ["web_m2x_options/static/src/components/*"]},
"assets": {
"web.assets_backend": [
"web_m2x_options/static/src/components/*",
"web_m2x_options/static/src/utils/mru.esm.js",
]
},
"installable": True,
}
2 changes: 2 additions & 0 deletions web_m2x_options/models/ir_config_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ def get_web_m2x_options(self):
"web_m2x_options.search_more",
"web_m2x_options.m2o_dialog",
"web_m2x_options.field_limit_entries",
"web_m2x_options.search_mru",
"web_m2x_options.search_mru_max_length",
]
values = self.sudo().search_read([["key", "in", opts]], ["key", "value"])
return {res["key"]: res["value"] for res in values}
17 changes: 17 additions & 0 deletions web_m2x_options/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ in the field's options dict

Deactivates the color picker on many2many_tags buttons to do nothing (ignored if open is set)

``search_mru`` *boolean* (Default: ``False``)

Allows to display to the user the 5 lasts records he selected for a given field. This list will be displayed
when the many2one field is focused and when the user didn't type anything to filter on. The values are stored in
the local storage of the browser and updated after a successful saved of the form. This features only works on form
views at the moment.

ir.config_parameter options
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -73,6 +80,15 @@ If you disable one option, you can enable it for particular field by setting "cr

Number of displayed lines on all One2many fields

``web_m2x_options.search_mru`` *boolean* (Default: default value is ``False``)

Enable MRU for all many2one fields (form view only).

``web_m2x_options.search_mru_max_length`` *int*

Changes the length of the records to store and show with MRU feature


To add these parameters go to Configuration -> Technical -> Parameters -> System Parameters and add new parameters like:

- web_m2x_options.create: False
Expand All @@ -81,6 +97,7 @@ To add these parameters go to Configuration -> Technical -> Parameters -> System
- web_m2x_options.limit: 10
- web_m2x_options.search_more: True
- web_m2x_options.field_limit_entries: 5
- web_m2x_options.search_mru: True


Example
Expand Down
89 changes: 89 additions & 0 deletions web_m2x_options/static/src/components/form.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import {
Many2ManyTagsFieldColorEditable,
} from "@web/views/fields/many2many_tags/many2many_tags_field";

import {
isMruGlobalOptionEnabled,
updateMruLocalStorageValues,
} from "@web_m2x_options/utils/mru.esm";

import {Dialog} from "@web/core/dialog/dialog";
import {FormController} from "@web/views/form/form_controller";
import {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog";
Expand Down Expand Up @@ -159,6 +164,7 @@ patch(Many2OneField.prototype, "web_m2x_options.Many2OneField", {
this._super(...arguments);
this.ir_options = Component.env.session.web_m2x_options;
},

/**
* @override
*/
Expand All @@ -170,6 +176,7 @@ patch(Many2OneField.prototype, "web_m2x_options.Many2OneField", {
searchMore: this.props.searchMore,
canCreate: this.props.canCreate,
nodeOptions: this.props.nodeOptions,
fieldName: this.props.name,
};
},

Expand Down Expand Up @@ -356,7 +363,24 @@ patch(FormController.prototype, "web_m2x_options.FormController", {
await self._setSubViewLimit();
self.superBeforeLoadResolver();
};
this.hasMru = self.hasMru();
},

hasMru() {
const activeFields = this.archInfo.activeFields;
for (const fieldName in activeFields) {
const fieldInfo = activeFields[fieldName];
if (
Boolean(fieldInfo) &&
Boolean(fieldInfo.options) &&
fieldInfo.options.search_mru
) {
return true;
}
}
return false;
},

/**
* @override
* add more method to add subview limit on formview
Expand Down Expand Up @@ -401,4 +425,69 @@ patch(FormController.prototype, "web_m2x_options.FormController", {
}
}
},

async saveButtonClicked() {
const mruChanges = this.getUpdateMruLocalStorageValues();
const saved = this._super(...arguments);
updateMruLocalStorageValues(this.props.resModel, mruChanges);
return saved;
},

async beforeExecuteActionButton() {
const mruChanges = this.getUpdateMruLocalStorageValues();
const saved = this._super(...arguments);
updateMruLocalStorageValues(this.props.resModel, mruChanges);
return saved;
},

async beforeLeave() {
const mruChanges = this.getUpdateMruLocalStorageValues();
const saved = this._super(...arguments);
updateMruLocalStorageValues(this.props.resModel, mruChanges);
return saved;
},

async onPagerUpdate() {
const mruChanges = this.getUpdateMruLocalStorageValues();
const saved = this._super(...arguments);
updateMruLocalStorageValues(this.props.resModel, mruChanges);
return saved;
},

getUpdateMruLocalStorageValues() {
if (!this.hasMru || !this.model.root.isDirty) {
return {};
}
const model = this.model;
if (model.__bm__) {
return {};
}
const changes = model.__bm__._generateChanges(
model.__bm__.localData[model.root.__bm_handle__],
{changesOnly: true}
);
const mruChanges = {};
let enableMru = false;
let nodeOptions = {};
let fieldInfo = {};
const activeFields = this.archInfo.activeFields;
Object.keys(changes).forEach(function (key) {
fieldInfo = activeFields[key];
if (
Boolean(fieldInfo) &&
Boolean(fieldInfo.FieldComponent) &&
fieldInfo.FieldComponent.name === "Many2OneField"
) {
nodeOptions = fieldInfo.options;
enableMru =
nodeOptions.search_mru !== undefined
? nodeOptions.search_mru
: isMruGlobalOptionEnabled();
if (enableMru) {
mruChanges[key] = changes[key];
}
}
});
return mruChanges;
},
});
56 changes: 47 additions & 9 deletions web_m2x_options/static/src/components/relational_utils.esm.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
/** @odoo-module **/

import {
getMruKey,
getMruValue,
isMruGlobalOptionEnabled,
} from "@web_m2x_options/utils/mru.esm";

import {Many2XAutocomplete} from "@web/views/fields/relational_utils";
import {patch} from "@web/core/utils/patch";
import {sprintf} from "@web/core/utils/strings";

const {Component} = owl;

export function is_option_set(option) {
Expand All @@ -16,6 +23,44 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", {
setup() {
this._super(...arguments);
this.ir_options = Component.env.session.web_m2x_options;
const searchMruOption = this.props.nodeOptions.search_mru;
this.enableMru =
searchMruOption === undefined
? isMruGlobalOptionEnabled()
: this.props.nodeOptions.search_mru;
if (this.enableMru) {
this.mruKey = getMruKey(this.env.model.root.resModel, this.props.fieldName);
}
},

async loadRecords(request) {
const withMru = this.enableMru && Boolean(!request);
this.lastProm = this.orm.call(this.props.resModel, "name_search", [], {
name: request,
operator: "ilike",
args: this.getLoadRecordsDomain(withMru),
limit: this.props.searchLimit + 1,
context: this.props.context,
});
const records = await this.lastProm;

if (withMru) {
const cachedIds = getMruValue(this.mruKey);
records.sort((record1, record2) => {
return cachedIds.indexOf(record1[0]) - cachedIds.indexOf(record2[0]);
});
}

return records;
},

getLoadRecordsDomain(withMru) {
const domain = this.props.getDomain();
const mruValue = withMru ? getMruValue(this.mruKey) : false;
if (Boolean(mruValue) && mruValue.length > 0) {
domain.push(["id", "in", mruValue]);
}
return domain;
},

async loadOptionsSource(request) {
Expand All @@ -24,6 +69,7 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", {
}
// Add options limit used to change number of selections record
// returned.

if (!_.isUndefined(this.ir_options["web_m2x_options.limit"])) {
this.props.searchLimit = parseInt(
this.ir_options["web_m2x_options.limit"],
Expand All @@ -41,15 +87,7 @@ patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", {
this.field_color = this.props.nodeOptions.field_color;
this.colors = this.props.nodeOptions.colors;

this.lastProm = this.orm.call(this.props.resModel, "name_search", [], {
name: request,
operator: "ilike",
args: this.props.getDomain(),
limit: this.props.searchLimit + 1,
context: this.props.context,
});
const records = await this.lastProm;

const records = await this.loadRecords(request);
var options = records.map((result) => ({
value: result[0],
id: result[0],
Expand Down
62 changes: 62 additions & 0 deletions web_m2x_options/static/src/utils/mru.esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/** @odoo-module **/
import {session} from "@web/session";

const LOCAL_STORAGE_NAME = "web_m2x_options_mru";

export function getMruMaxLength() {
return (
parseInt(session.web_m2x_options["web_m2x_options.search_mru_max_length"]) || 5
);
}

export function isMruGlobalOptionEnabled() {
return session.web_m2x_options["web_m2x_options.search_mru"] === "True";
}

export function getMruKey(modelName, fieldName) {
return session.db + "/" + modelName + "/" + fieldName;
}

export function getMruStorage() {
let data = localStorage.getItem(LOCAL_STORAGE_NAME);
if (!data) {
return {};
}
data = JSON.parse(data);
return data;
}

export function getMruValue(mruKey) {
const data = getMruStorage();
return mruKey in data ? data[mruKey] : [];
}

export function setMruValue(mruKey, value) {
const data = getMruStorage();
data[mruKey] = value;
localStorage.setItem(LOCAL_STORAGE_NAME, JSON.stringify(data));
}

export function updateMruIds(mruKey, recordId) {
if (!recordId) {
return;
}
const cachedIds = getMruValue(mruKey);
const currentIndex = cachedIds.indexOf(recordId);
if (currentIndex !== -1) {
cachedIds.splice(currentIndex, 1);
}
cachedIds.unshift(recordId);
const maxLength = getMruMaxLength();
if (cachedIds.length > maxLength) {
cachedIds.splice(maxLength, cachedIds.length - maxLength);
}
setMruValue(mruKey, cachedIds);
}

export function updateMruLocalStorageValues(modelName, values) {
Object.keys(values).forEach(function (key) {
const mruKey = getMruKey(modelName, key);
updateMruIds(mruKey, values[key]);
});
}
Loading