fields
forms
Res User Group Ids
Odoo 19 fields — Res User Group Ids (webclient)
Live preview
Interactive
Source excerpt
web/static/src/webclient/res_user_group_ids_field/res_user_group_ids_field.js
import { _t } from "@web/core/l10n/translation";
import { x2ManyCommands } from "@web/core/orm_service";
import { registry } from "@web/core/registry";
import { deepCopy } from "@web/core/utils/objects";
import { parseXML } from "@web/core/utils/xml";
import { Record } from "@web/model/record";
import { standardFieldProps } from "@web/views/fields/standard_field_props";
import { FormArchParser } from "@web/views/form/form_arch_parser";
import { FormRenderer } from "@web/views/form/form_renderer";
import { Component, onWillRender, toRaw, useChildSubEnv } from "@odoo/owl";
/**
* This widget is only used for the 'group_ids' field of the 'res.users'
* form view or the 'implied_ids' field of the 'res.groups' form view,
* in order to vizualize and configure access rights.
*/
class ResUserGroupIdsField extends Component {
static template = "web.ResUserGroupIdsField";
static components = { Record, FormRenderer };
static props = { ...standardFieldProps };
setup() {
const { groups, privileges, categories } = toRaw(
this.props.record.data.view_group_hierarchy
);
// Generate the "other" category (for privileges that do not belong to any category)
const privilegesWithoutCategory = Object.values(privileges)
.filter((privilege) => !privilege.category_id)
.sort((privilege) => privilege.sequence);
if (privilegesWithoutCategory.length) {
categories.push({
id: "other",
name: _t("Other"),
privilege_ids: privilegesWithoutCategory.map((privilege) => privilege.id),
});
}
// Generate the extra rights category (for groups without privilege)
this.extraCategory = {
id: "extra",
name: _t("Extra Rights"),
privileges: Object.values(groups)
.filter((group) => !group.privilege_id)
.map((group) => {
const privilege = {
description: group.comment,
groupId: group.id,
id: "group_" + group.id,
name: group.name,
};
privilege.groupFieldName = this.getFieldName(privilege);
return privilege;
})
.sort((p1, p2) => p1.name.localeCompare(p2.name)),
};
// Generate selection (for privileges) and boolean (for extra right groups) fields
this._fields = {};
const booleanFieldToGroupId = {};
for (const category of categories) {
category.privileges = [];
for (const privilegeId of category.privilege_ids) {
const privilege = privileges[privilegeId];
category.privileges.push(privilege);
const helpLines = privilege.description ? [privilege.description] : [];
for (const gid of privilege.group_ids) {
if (groups[gid].comment) {
helpLines.push(`- ${groups[gid].name}: ${groups[gid].comment}`);
}
}
const selection = privilege.group_ids.map((gId) => [gId, groups[gId].name]);
selection.unshift([false, privilege.placeholder || ""]);
this._fields[this.getFieldName(privilege)] = {
help: helpLines.join("\n"),
selection,
string: privilege.name,
type: "selection",
};
}
}
for (const privilege of this.extraCategory.privileges) {
this._fields[privilege.groupFieldName] = {
help: privilege.description,
string: privilege.name,
type: "boolean",
};
booleanFieldToGroupId[privilege.groupFieldName] = privilege.groupId;
}
this.fields = deepCopy(this._fields); // dynamically modifed before each rendering w.r.t. to current groups
// Generate archInfo to provide to the FormRenderer
const models = { main: { fields: this._fields } };
const arch = `
<t>
<group>
${categories.map((category) => this.getCategoryArch(category)).join("")}
</group>
${odoo.debug ? this.getExtraGroupsArch() : ""}
</t>`;
this.archInfo = new FormArchParser().parse(parseXML(arch), models, "main");
// Generate information to share through the env with "res_user_group_ids_privilege" widgets
// - `booleanFieldToGroupId` maps generated boolean field names to their group id
// - `privileges` is an object mapping all privilege ids to their description
// - `groups` is an object mapping all group ids to their description, which is based on
// the current selected groups
this.info = {
booleanFieldToGroupId,
groups: {},
privileges,
};
useChildSubEnv({
resUserGroupsInfo: this.info, // computed in onWillRender
});
onWillRender(() => {
// Generate groups information based on current ids, i.e.
// - `id`, `name`, `privilege_id`, `comment` are kept as in the static definition
// - `selected` is true iff the group is explicitely selected (!= implied)
Registry / API
- Registry name
res_user_group_ids- Category
fields- Module
web- Slug
res-user-group-ids- Nav group
forms