From bd49f9fd17aa6661945a3eff212ceabedf20efb8 Mon Sep 17 00:00:00 2001 From: Yannick Tivisse Date: Wed, 25 Apr 2018 15:46:05 +0200 Subject: [PATCH] [IMP] base: Remove the reified view on 'groups_id' in res_users' form Now all the group fields are defined on the res.users model. No need to generate a reified view according to the groups definitions. --- odoo/addons/base/models/res_users.py | 296 +-------------------------- 1 file changed, 5 insertions(+), 291 deletions(-) diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py index 9f9a1fa3281..8664e70c556 100644 --- a/odoo/addons/base/models/res_users.py +++ b/odoo/addons/base/models/res_users.py @@ -26,48 +26,23 @@ USER_PRIVATE_FIELDS = ['password'] concat = chain.from_iterable # -# Functions for manipulating boolean and selection pseudo-fields +# Functions for manipulating boolean and selection fields # -def name_boolean_group(id): - return 'in_group_' + str(id) -def name_selection_groups(ids): - return 'sel_groups_' + '_'.join(str(it) for it in ids) def is_boolean_group(name): - return name.startswith('in_group_') + return name.startswith('has_group_') + def is_selection_groups(name): - return name.startswith('sel_groups_') + return name.startswith('group_') -def is_reified_group(name): - return is_boolean_group(name) or is_selection_groups(name) - -def get_boolean_group(name): - return int(name[9:]) - -def get_selection_groups(name): - return [int(v) for v in name[11:].split('_')] - -def parse_m2m(commands): - "return a list of ids corresponding to a many2many value" - ids = [] - for command in commands: - if isinstance(command, (tuple, list)): - if command[0] in (1, 4): - ids.append(command[1]) - elif command[0] == 5: - ids = [] - elif command[0] == 6: - ids = list(command[2]) - else: - ids.append(command) - return ids #---------------------------------------------------------- # Basic res.groups and res.users #---------------------------------------------------------- + class Groups(models.Model): _name = "res.groups" _description = "Access Groups" @@ -825,267 +800,6 @@ class UsersImplied(models.Model): super(UsersImplied, self).write(vals) return res -# -# Virtual checkbox and selection for res.user form view -# -# Extension of res.groups and res.users for the special groups view in the users -# form. This extension presents groups with selection and boolean widgets: -# - Groups are shown by application, with boolean and/or selection fields. -# Selection fields typically defines a role "Name" for the given application. -# - Uncategorized groups are presented as boolean fields and grouped in a -# section "Others". -# -# The user form view is modified by an inherited view (base.user_groups_view); -# the inherited view replaces the field 'groups_id' by a set of reified group -# fields (boolean or selection fields). The arch of that view is regenerated -# each time groups are changed. -# -# Naming conventions for reified groups fields: -# - boolean field 'in_group_ID' is True iff -# ID is in 'groups_id' -# - selection field 'sel_groups_ID1_..._IDk' is ID iff -# ID is in 'groups_id' and ID is maximal in the set {ID1, ..., IDk} -# - -class GroupsView(models.Model): - _inherit = 'res.groups' - - @api.model - def create(self, values): - user = super(GroupsView, self).create(values) - self._update_user_groups_view() - # actions.get_bindings() depends on action records - self.env['ir.actions.actions'].clear_caches() - return user - - @api.multi - def write(self, values): - res = super(GroupsView, self).write(values) - self._update_user_groups_view() - # actions.get_bindings() depends on action records - self.env['ir.actions.actions'].clear_caches() - return res - - @api.multi - def unlink(self): - res = super(GroupsView, self).unlink() - self._update_user_groups_view() - # actions.get_bindings() depends on action records - self.env['ir.actions.actions'].clear_caches() - return res - - @api.model - def _update_user_groups_view(self): - """ Modify the view with xmlid ``base.user_groups_view``, which inherits - the user form view, and introduces the reified group fields. - """ - if self._context.get('install_mode'): - # use installation/admin language for translatable names in the view - user_context = self.env['res.users'].context_get() - self = self.with_context(**user_context) - - # We have to try-catch this, because at first init the view does not - # exist but we are already creating some basic groups. - view = self.env.ref('base.user_groups_view', raise_if_not_found=False) - if view and view.exists() and view._name == 'ir.ui.view': - group_no_one = view.env.ref('base.group_no_one') - xml1, xml2 = [], [] - xml1.append(E.separator(string=_('Application Accesses'), colspan="2")) - for app, kind, gs in self.get_groups_by_application(): - # hide groups in categories 'Hidden' and 'Extra' (except for group_no_one) - attrs = {} - if app.xml_id in ('base.module_category_hidden', 'base.module_category_extra', 'base.module_category_usability'): - attrs['groups'] = 'base.group_no_one' - - if kind == 'selection': - # application name with a selection field - field_name = name_selection_groups(gs.ids) - xml1.append(E.field(name=field_name, **attrs)) - xml1.append(E.newline()) - else: - # application separator with boolean fields - app_name = app.name or _('Other') - xml2.append(E.separator(string=app_name, colspan="4", **attrs)) - for g in gs: - field_name = name_boolean_group(g.id) - if g == group_no_one: - # make the group_no_one invisible in the form view - xml2.append(E.field(name=field_name, invisible="1", **attrs)) - else: - xml2.append(E.field(name=field_name, **attrs)) - - xml2.append({'class': "o_label_nowrap"}) - xml = E.field(E.group(*(xml1), col="2"), E.group(*(xml2), col="4"), name="groups_id", position="replace") - xml.addprevious(etree.Comment("GENERATED AUTOMATICALLY BY GROUPS")) - xml_content = etree.tostring(xml, pretty_print=True, encoding="unicode") - - new_context = dict(view._context) - new_context.pop('install_mode_data', None) # don't set arch_fs for this computed view - new_context['lang'] = None - view.with_context(new_context).write({'arch': xml_content}) - - def get_application_groups(self, domain): - """ Return the non-share groups that satisfy ``domain``. """ - return self.search(domain + [('share', '=', False)]) - - @api.model - def get_groups_by_application(self): - """ Return all groups classified by application (module category), as a list:: - - [(app, kind, groups), ...], - - where ``app`` and ``groups`` are recordsets, and ``kind`` is either - ``'boolean'`` or ``'selection'``. Applications are given in sequence - order. If ``kind`` is ``'selection'``, ``groups`` are given in - reverse implication order. - """ - def linearize(app, gs): - # determine sequence order: a group appears after its implied groups - order = {g: len(g.trans_implied_ids & gs) for g in gs} - # check whether order is total, i.e., sequence orders are distinct - if len(set(order.values())) == len(gs): - return (app, 'selection', gs.sorted(key=order.get)) - else: - return (app, 'boolean', gs) - - # classify all groups by application - by_app, others = defaultdict(self.browse), self.browse() - for g in self.get_application_groups([]): - if g.category_id: - by_app[g.category_id] += g - else: - others += g - # build the result - res = [] - for app, gs in sorted(by_app.items(), key=lambda it: it[0].sequence or 0): - res.append(linearize(app, gs)) - if others: - res.append((self.env['ir.module.category'], 'boolean', others)) - return res - - -class UsersView(models.Model): - _inherit = 'res.users' - - @api.model - def create(self, values): - values = self._remove_reified_groups(values) - user = super(UsersView, self).create(values) - group_multi_company = self.env.ref('base.group_multi_company', False) - if group_multi_company and 'company_ids' in values: - if len(user.company_ids) <= 1 and user.id in group_multi_company.users.ids: - group_multi_company.write({'users': [(3, user.id)]}) - elif len(user.company_ids) > 1 and user.id not in group_multi_company.users.ids: - group_multi_company.write({'users': [(4, user.id)]}) - return user - - @api.multi - def write(self, values): - values = self._remove_reified_groups(values) - res = super(UsersView, self).write(values) - group_multi_company = self.env.ref('base.group_multi_company', False) - if group_multi_company and 'company_ids' in values: - for user in self: - if len(user.company_ids) <= 1 and user.id in group_multi_company.users.ids: - group_multi_company.write({'users': [(3, user.id)]}) - elif len(user.company_ids) > 1 and user.id not in group_multi_company.users.ids: - group_multi_company.write({'users': [(4, user.id)]}) - return res - - def _remove_reified_groups(self, values): - """ return `values` without reified group fields """ - add, rem = [], [] - values1 = {} - - for key, val in values.items(): - if is_boolean_group(key): - (add if val else rem).append(get_boolean_group(key)) - elif is_selection_groups(key): - rem += get_selection_groups(key) - if val: - add.append(val) - else: - values1[key] = val - - if 'groups_id' not in values and (add or rem): - # remove group ids in `rem` and add group ids in `add` - values1['groups_id'] = list(itertools.chain( - pycompat.izip(repeat(3), rem), - pycompat.izip(repeat(4), add) - )) - - return values1 - - @api.model - def default_get(self, fields): - group_fields, fields = partition(is_reified_group, fields) - fields1 = (fields + ['groups_id']) if group_fields else fields - values = super(UsersView, self).default_get(fields1) - self._add_reified_groups(group_fields, values) - return values - - @api.multi - def read(self, fields=None, load='_classic_read'): - # determine whether reified groups fields are required, and which ones - fields1 = fields or list(self.fields_get()) - group_fields, other_fields = partition(is_reified_group, fields1) - - # read regular fields (other_fields); add 'groups_id' if necessary - drop_groups_id = False - if group_fields and fields: - if 'groups_id' not in other_fields: - other_fields.append('groups_id') - drop_groups_id = True - else: - other_fields = fields - - res = super(UsersView, self).read(other_fields, load=load) - - # post-process result to add reified group fields - if group_fields: - for values in res: - self._add_reified_groups(group_fields, values) - if drop_groups_id: - values.pop('groups_id', None) - return res - - def _add_reified_groups(self, fields, values): - """ add the given reified group fields into `values` """ - gids = set(parse_m2m(values.get('groups_id') or [])) - for f in fields: - if is_boolean_group(f): - values[f] = get_boolean_group(f) in gids - elif is_selection_groups(f): - selected = [gid for gid in get_selection_groups(f) if gid in gids] - values[f] = selected and selected[-1] or False - - @api.model - def fields_get(self, allfields=None, attributes=None): - res = super(UsersView, self).fields_get(allfields, attributes=attributes) - # add reified groups fields - for app, kind, gs in self.env['res.groups'].sudo().get_groups_by_application(): - if kind == 'selection': - # selection group field - tips = ['%s: %s' % (g.name, g.comment) for g in gs if g.comment] - res[name_selection_groups(gs.ids)] = { - 'type': 'selection', - 'string': app.name or _('Other'), - 'selection': [(False, '')] + [(g.id, g.name) for g in gs], - 'help': '\n'.join(tips), - 'exportable': False, - 'selectable': False, - } - else: - # boolean group fields - for g in gs: - res[name_boolean_group(g.id)] = { - 'type': 'boolean', - 'string': g.name, - 'help': g.comment, - 'exportable': False, - 'selectable': False, - } - return res #---------------------------------------------------------- # change password wizard