[MERGE] res_users res_group rework
bzr revid: al@openerp.com-20110805205527-hjsjwia9fhf57rkp
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
|
||||
{
|
||||
'name': 'Base',
|
||||
'version': '1.3',
|
||||
'version': '1.4',
|
||||
'category': 'Generic Modules/Base',
|
||||
'complexity': "easy",
|
||||
'description': """The kernel of OpenERP, needed for all installation.""",
|
||||
|
||||
@@ -1053,7 +1053,6 @@
|
||||
|
||||
<record model="res.users" id="base.user_root">
|
||||
<field name="signature">Administrator</field>
|
||||
<field name="address_id" ref="main_address"/>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
<field name="menu_id" ref="action_menu_admin"/>
|
||||
<field name="company_ids" eval="[(4, ref('main_company'))]"/>
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
<field name="password">demo</field>
|
||||
<field name="name">Demo User</field>
|
||||
<field name="signature">Mr Demo</field>
|
||||
<field name="address_id" ref="demo_address"/>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
<field name="groups_id" eval="[(6,0,[ref('base.group_user')])]"/>
|
||||
</record>
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
<page string="Users">
|
||||
<field colspan="4" name="users" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Inherited">
|
||||
<label colspan="4" string="Users added to this group are automatically added in the following groups."/>
|
||||
<field colspan="4" name="implied_ids" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Menus">
|
||||
<field colspan="4" name="menu_access" nolabel="1"/>
|
||||
</page>
|
||||
@@ -77,25 +81,23 @@
|
||||
<field eval="18" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Users">
|
||||
<notebook colspan="4">
|
||||
<page string="Current Activity">
|
||||
<field name="company_id" widget="selection" readonly="0"
|
||||
groups="base.group_multi_company"
|
||||
on_change="on_change_company_id(company_id)" />
|
||||
<field name="view" readonly="0"/>
|
||||
<label string="" colspan="2"/>
|
||||
<separator string="Default Filters" colspan="4"/>
|
||||
<newline/>
|
||||
</page>
|
||||
<page string="Preferences">
|
||||
<field name="context_lang" completion="1" readonly="0"/>
|
||||
<field name="context_tz" completion="1" readonly="0"/>
|
||||
<field name="menu_tips" colspan="2" readonly="0"/>
|
||||
<separator string="Email & Signature" colspan="4"/>
|
||||
<group colspan="4"><field name="user_email" widget="email" readonly="0"/></group>
|
||||
<field colspan="4" name="signature" readonly="0" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<field name="name"/>
|
||||
<newline/>
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Preferences" colspan="2"/>
|
||||
<field name="view"/>
|
||||
<field name="context_lang"/>
|
||||
<field name="context_tz"/>
|
||||
<field name="menu_tips"/>
|
||||
</group>
|
||||
<group name="default_filters" colspan="2" col="2">
|
||||
<separator string="Default Filters" colspan="2"/>
|
||||
<field name="company_id" widget="selection"
|
||||
groups="base.group_multi_company" on_change="on_change_company_id(company_id)"/>
|
||||
</group>
|
||||
<separator string="Email & Signature" colspan="2"/>
|
||||
<field colspan="4" name="user_email" widget="email"/>
|
||||
<field colspan="4" name="signature" nolabel="1"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
@@ -106,45 +108,40 @@
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Users">
|
||||
<field name="name" select="1"/>
|
||||
<field name="active"/>
|
||||
<field name="login" select="1"/>
|
||||
<field name="new_password" password="True"/>
|
||||
<newline/>
|
||||
<group colspan="4" col="6">
|
||||
<field name="name" select="1"/>
|
||||
<field name="login" select="1"/>
|
||||
<field name="new_password" password="True"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="User">
|
||||
<group colspan="1" col="2">
|
||||
<separator string="Contact" colspan="2"/>
|
||||
<separator string="Preferences" colspan="2"/>
|
||||
<field name="context_lang"/>
|
||||
<field name="context_tz"/>
|
||||
<field name="menu_tips"/>
|
||||
</group>
|
||||
<group name="default_filters" colspan="1" col="2">
|
||||
<separator string="Default Filters" colspan="2"/>
|
||||
<field name="company_id" required="1"
|
||||
context="{'user_preference': 0}"
|
||||
groups="base.group_multi_company"
|
||||
/>
|
||||
<field name="address_id"/>
|
||||
<field name="user_email" widget="email"/>
|
||||
groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group colspan="1" col="2" groups="base.group_extended">
|
||||
<separator string="Action" colspan="2"/>
|
||||
<field name="action_id"/>
|
||||
<field domain="[('usage','=','menu')]" name="menu_id" required="True"/>
|
||||
</group>
|
||||
<group colspan="1" col="2">
|
||||
<separator string="Preferences" colspan="2"/>
|
||||
<field name="context_lang"/>
|
||||
<field name="context_tz"/>
|
||||
<group colspan="2" col="4">
|
||||
<field name="view" readonly="0"/>
|
||||
<field name="menu_tips" colspan="2"/>
|
||||
</group>
|
||||
</group>
|
||||
<newline/>
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Signature" colspan="2"/>
|
||||
<field colspan="2" name="signature" nolabel="1"/>
|
||||
</group>
|
||||
<group colspan="2" col="2" expand="1">
|
||||
<separator string="Groups" colspan="2"/>
|
||||
<field colspan="2" nolabel="1" name="groups_id"/>
|
||||
<separator string="Email & Signature" colspan="2"/>
|
||||
<group colspan="4">
|
||||
<field name="user_email" widget="email"/>
|
||||
</group>
|
||||
<field colspan="4" name="signature" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Access Rights">
|
||||
<field nolabel="1" name="groups_id"/>
|
||||
</page>
|
||||
<page string="Companies" groups="base.group_multi_company">
|
||||
<field colspan="4" nolabel="1" name="company_ids" select="1"/>
|
||||
@@ -175,7 +172,6 @@
|
||||
<search string="Users">
|
||||
<field name="name"/>
|
||||
<field name="login"/>
|
||||
<field name="address_id" string="Address"/>
|
||||
<field name="company_ids" string="Company" groups="base.group_multi_company"/>
|
||||
</search>
|
||||
</field>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2011 OpenERP s.a. (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
def migrate(cr, version):
|
||||
"copy emails from user.address_id.email to user.user_email"
|
||||
cr.execute("""
|
||||
UPDATE res_users
|
||||
SET user_email = res_partner_address.email
|
||||
FROM res_partner_address
|
||||
WHERE res_users.address_id = res_partner_address.id""")
|
||||
|
||||
@@ -29,13 +29,16 @@ import pooler
|
||||
from tools.translate import _
|
||||
from service import security
|
||||
import netsvc
|
||||
import logging
|
||||
from lxml import etree
|
||||
|
||||
class groups(osv.osv):
|
||||
_name = "res.groups"
|
||||
_order = 'name'
|
||||
_description = "Access Groups"
|
||||
_columns = {
|
||||
'name': fields.char('Group Name', size=64, required=True),
|
||||
'name': fields.char('Group Name', size=64, required=True, translate=True),
|
||||
'users': fields.many2many('res.users', 'res_groups_users_rel', 'gid', 'uid', 'Users'),
|
||||
'model_access': fields.one2many('ir.model.access', 'group_id', 'Access Controls'),
|
||||
'rule_groups': fields.many2many('ir.rule', 'rule_group_rel',
|
||||
'group_id', 'rule_group_id', 'Rules', domain=[('global', '=', False)]),
|
||||
@@ -76,6 +79,21 @@ class groups(osv.osv):
|
||||
aid.write({'groups_id': [(4, gid)]})
|
||||
return gid
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
group_users = []
|
||||
for record in self.read(cr, uid, ids, ['users'], context=context):
|
||||
if record['users']:
|
||||
group_users.extend(record['users'])
|
||||
if group_users:
|
||||
user_names = [user.name for user in self.pool.get('res.users').browse(cr, uid, group_users, context=context)]
|
||||
user_names = list(set(user_names))
|
||||
if len(user_names) >= 5:
|
||||
user_names = user_names[:5] + ['...']
|
||||
raise osv.except_osv(_('Warning !'),
|
||||
_('Group(s) cannot be deleted, because some user(s) still belong to them: %s !') % \
|
||||
', '.join(user_names))
|
||||
return super(groups, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
def get_extended_interface_group(self, cr, uid, context=None):
|
||||
data_obj = self.pool.get('ir.model.data')
|
||||
extended_group_data_id = data_obj._get_id(cr, uid, 'base', 'group_extended')
|
||||
@@ -175,25 +193,6 @@ class users(osv.osv):
|
||||
extended_users = group_obj.read(cr, uid, extended_group_id, ['users'], context=context)['users']
|
||||
return dict(zip(ids, ['extended' if user in extended_users else 'simple' for user in ids]))
|
||||
|
||||
def _email_get(self, cr, uid, ids, name, arg, context=None):
|
||||
# perform this as superuser because the current user is allowed to read users, and that includes
|
||||
# the email, even without any direct read access on the res_partner_address object.
|
||||
return dict([(user.id, user.address_id.email) for user in self.browse(cr, 1, ids)]) # no context to avoid potential security issues as superuser
|
||||
|
||||
def _email_set(self, cr, uid, ids, name, value, arg, context=None):
|
||||
if not isinstance(ids,list):
|
||||
ids = [ids]
|
||||
address_obj = self.pool.get('res.partner.address')
|
||||
for user in self.browse(cr, uid, ids, context=context):
|
||||
# perform this as superuser because the current user is allowed to write to the user, and that includes
|
||||
# the email even without any direct write access on the res_partner_address object.
|
||||
if user.address_id:
|
||||
address_obj.write(cr, 1, user.address_id.id, {'email': value or None}) # no context to avoid potential security issues as superuser
|
||||
else:
|
||||
address_id = address_obj.create(cr, 1, {'name': user.name, 'email': value or None}) # no context to avoid potential security issues as superuser
|
||||
self.write(cr, uid, ids, {'address_id': address_id}, context)
|
||||
return True
|
||||
|
||||
def _set_new_password(self, cr, uid, id, name, value, args, context=None):
|
||||
if value is False:
|
||||
# Do not update the password if no value is provided, ignore silently.
|
||||
@@ -220,13 +219,8 @@ class users(osv.osv):
|
||||
fnct_inv=_set_new_password,
|
||||
string='Change password', help="Only specify a value if you want to change the user password. "
|
||||
"This user will have to logout and login again!"),
|
||||
'email': fields.char('E-mail', size=64,
|
||||
help='If an email is provided, the user will be sent a message '
|
||||
'welcoming him.\n\nWarning: if "email_from" and "smtp_server"'
|
||||
" aren't configured, it won't be possible to email new "
|
||||
"users."),
|
||||
'user_email': fields.char('E-mail', size=64),
|
||||
'signature': fields.text('Signature', size=64),
|
||||
'address_id': fields.many2one('res.partner.address', 'Address'),
|
||||
'active': fields.boolean('Active'),
|
||||
'action_id': fields.many2one('ir.actions.actions', 'Home Action', help="If specified, this action will be opened at logon for this user, in addition to the standard menu."),
|
||||
'menu_id': fields.many2one('ir.actions.actions', 'Menu Action', help="If specified, the action will replace the standard menu for this user."),
|
||||
@@ -247,7 +241,6 @@ class users(osv.osv):
|
||||
'view': fields.function(_get_interface_type, method=True, type='selection', fnct_inv=_set_interface_type,
|
||||
selection=[('simple','Simplified'),('extended','Extended')],
|
||||
string='Interface', help="OpenERP offers a simplified and an extended user interface. If you use OpenERP for the first time we strongly advise you to select the simplified interface, which has less features but is easier to use. You can switch to the other interface from the User/Preferences menu at any time."),
|
||||
'user_email': fields.function(_email_get, method=True, fnct_inv=_email_set, string='Email', type="char", size=240),
|
||||
'menu_tips': fields.boolean('Menu Tips', help="Check out this box if you want to always display tips on each menu action"),
|
||||
'date': fields.datetime('Last Connection', readonly=True),
|
||||
}
|
||||
@@ -346,7 +339,6 @@ class users(osv.osv):
|
||||
'company_id': _get_company,
|
||||
'company_ids': _get_companies,
|
||||
'groups_id': _get_group,
|
||||
'address_id': False,
|
||||
'menu_tips':True
|
||||
}
|
||||
|
||||
@@ -414,7 +406,6 @@ class users(osv.osv):
|
||||
copy_pattern = _("%s (copy)")
|
||||
copydef = dict(login=(copy_pattern % user2copy['login']),
|
||||
name=(copy_pattern % user2copy['name']),
|
||||
address_id=False, # avoid sharing the address of the copied user!
|
||||
)
|
||||
copydef.update(default)
|
||||
return super(users, self).copy(cr, uid, id, copydef, context)
|
||||
@@ -510,30 +501,6 @@ class users(osv.osv):
|
||||
|
||||
users()
|
||||
|
||||
class groups2(osv.osv): ##FIXME: Is there a reason to inherit this object ?
|
||||
_inherit = 'res.groups'
|
||||
_columns = {
|
||||
'users': fields.many2many('res.users', 'res_groups_users_rel', 'gid', 'uid', 'Users'),
|
||||
}
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
group_users = []
|
||||
for record in self.read(cr, uid, ids, ['users'], context=context):
|
||||
if record['users']:
|
||||
group_users.extend(record['users'])
|
||||
|
||||
if group_users:
|
||||
user_names = [user.name for user in self.pool.get('res.users').browse(cr, uid, group_users, context=context)]
|
||||
user_names = list(set(user_names))
|
||||
if len(user_names) >= 5:
|
||||
user_names = user_names[:5] + ['...']
|
||||
raise osv.except_osv(_('Warning !'),
|
||||
_('Group(s) cannot be deleted, because some user(s) still belong to them: %s !') % \
|
||||
', '.join(user_names))
|
||||
return super(groups2, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
groups2()
|
||||
|
||||
class res_config_view(osv.osv_memory):
|
||||
_name = 'res.config.view'
|
||||
_inherit = 'res.config'
|
||||
@@ -554,4 +521,291 @@ class res_config_view(osv.osv_memory):
|
||||
|
||||
res_config_view()
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Extension of res.groups and res.users with a relation for "implied" or
|
||||
# "inherited" groups. Once a user belongs to a group, it automatically belongs
|
||||
# to the implied groups (transitively).
|
||||
#
|
||||
|
||||
class groups_implied(osv.osv):
|
||||
_inherit = 'res.groups'
|
||||
_columns = {
|
||||
'implied_ids': fields.many2many('res.groups', 'res_groups_implied_rel', 'gid', 'hid',
|
||||
string='Inherits', help='Users of this group automatically inherit those groups'),
|
||||
}
|
||||
|
||||
def get_closure(self, cr, uid, ids, context=None):
|
||||
"return the closure of ids, i.e., all groups recursively implied by ids"
|
||||
closure = set()
|
||||
todo = self.browse(cr, 1, ids)
|
||||
while todo:
|
||||
g = todo.pop()
|
||||
if g.id not in closure:
|
||||
closure.add(g.id)
|
||||
todo.extend(g.implied_ids)
|
||||
return list(closure)
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
users = values.pop('users', None)
|
||||
gid = super(groups_implied, self).create(cr, uid, values, context)
|
||||
if users:
|
||||
# delegate addition of users to add implied groups
|
||||
self.write(cr, uid, [gid], {'users': users}, context)
|
||||
return gid
|
||||
|
||||
def write(self, cr, uid, ids, values, context=None):
|
||||
res = super(groups_implied, self).write(cr, uid, ids, values, context)
|
||||
if values.get('users') or values.get('implied_ids'):
|
||||
# add implied groups (to all users of each group)
|
||||
for g in self.browse(cr, uid, ids):
|
||||
gids = self.get_closure(cr, uid, [g.id], context)
|
||||
users = [(4, u.id) for u in g.users]
|
||||
super(groups_implied, self).write(cr, uid, gids, {'users': users}, context)
|
||||
return res
|
||||
|
||||
def get_maximal(self, cr, uid, ids, context=None):
|
||||
"return the maximal element among the group ids"
|
||||
max_set, max_closure = set(), set()
|
||||
for gid in ids:
|
||||
if gid not in max_closure:
|
||||
closure = set(self.get_closure(cr, uid, [gid], context))
|
||||
max_set -= closure # remove implied groups from max_set
|
||||
max_set.add(gid) # gid is maximal
|
||||
max_closure |= closure # update closure of max_set
|
||||
if len(max_set) > 1:
|
||||
log = logging.getLogger('res.groups')
|
||||
log.warning('Groups %s are maximal among %s, only one expected.', max_set, ids)
|
||||
return bool(max_set) and max_set.pop()
|
||||
|
||||
groups_implied()
|
||||
|
||||
class users_implied(osv.osv):
|
||||
_inherit = 'res.users'
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
groups = values.pop('groups_id')
|
||||
user_id = super(users_implied, self).create(cr, uid, values, context)
|
||||
if groups:
|
||||
# delegate addition of groups to add implied groups
|
||||
self.write(cr, uid, [user_id], {'groups_id': groups}, context)
|
||||
return user_id
|
||||
|
||||
def write(self, cr, uid, ids, values, context=None):
|
||||
if not isinstance(ids,list):
|
||||
ids = [ids]
|
||||
res = super(users_implied, self).write(cr, uid, ids, values, context)
|
||||
if values.get('groups_id'):
|
||||
# add implied groups for all users
|
||||
groups_obj = self.pool.get('res.groups')
|
||||
for u in self.browse(cr, uid, ids):
|
||||
old_gids = map(int, u.groups_id)
|
||||
new_gids = groups_obj.get_closure(cr, uid, old_gids, context)
|
||||
if len(old_gids) != len(new_gids):
|
||||
values = {'groups_id': [(6, 0, new_gids)]}
|
||||
super(users_implied, self).write(cr, uid, [u.id], values, context)
|
||||
return res
|
||||
|
||||
users_implied()
|
||||
|
||||
|
||||
|
||||
#
|
||||
# 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 named as "App/Name" (corresponding to root menu "App") are presented
|
||||
# per application, with one boolean and selection field each. The selection
|
||||
# field defines a role "Name" for the given application.
|
||||
# - Groups named as "Stuff/Name" are presented as boolean fields and grouped
|
||||
# under sections "Stuff".
|
||||
# - The remaining groups are presented as boolean fields and grouped in a
|
||||
# section "Others".
|
||||
#
|
||||
|
||||
class groups_view(osv.osv):
|
||||
_inherit = 'res.groups'
|
||||
|
||||
def get_classified(self, cr, uid, context=None):
|
||||
""" classify all groups by prefix; return a pair (apps, others) where
|
||||
- both are lists like [("App", [("Name", browse_group), ...]), ...];
|
||||
- apps is sorted in menu order;
|
||||
- others are sorted in alphabetic order;
|
||||
- groups not like App/Name are at the end of others, under _('Others')
|
||||
"""
|
||||
# sort groups by implication, with implied groups first
|
||||
groups = self.browse(cr, uid, self.search(cr, uid, []), context)
|
||||
groups.sort(key=lambda g: set(self.get_closure(cr, uid, [g.id], context)))
|
||||
|
||||
# classify groups depending on their names
|
||||
classified = {}
|
||||
for g in groups:
|
||||
# split() returns 1 or 2 elements, so names[-2] is prefix or None
|
||||
names = [None] + [s.strip() for s in g.name.split('/', 1)]
|
||||
classified.setdefault(names[-2], []).append((names[-1], g))
|
||||
|
||||
# determine the apps (that correspond to root menus, in order)
|
||||
menu_obj = self.pool.get('ir.ui.menu')
|
||||
menu_ids = menu_obj.search(cr, uid, [('parent_id','=',False)], context={'ir.ui.menu.full_list': True})
|
||||
apps = []
|
||||
for m in menu_obj.browse(cr, uid, menu_ids, context):
|
||||
if m.name in classified:
|
||||
# application groups are already sorted by implication
|
||||
apps.append((m.name, classified.pop(m.name)))
|
||||
|
||||
# other groups
|
||||
others = sorted(classified.items(), key=lambda pair: pair[0])
|
||||
if others and others[0][0] is None:
|
||||
others.append((_('Others'), others.pop(0)[1]))
|
||||
for sec, groups in others:
|
||||
groups.sort(key=lambda pair: pair[0])
|
||||
|
||||
return (apps, others)
|
||||
|
||||
groups_view()
|
||||
|
||||
# Naming conventions for reified groups fields:
|
||||
# - boolean field 'in_group_ID' is True iff
|
||||
# ID is in 'groups_id'
|
||||
# - boolean field 'in_groups_ID1_..._IDk' is True iff
|
||||
# any of ID1, ..., IDk 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}
|
||||
|
||||
def name_boolean_group(id): return 'in_group_' + str(id)
|
||||
def name_boolean_groups(ids): return 'in_groups_' + '_'.join(map(str, ids))
|
||||
def name_selection_groups(ids): return 'sel_groups_' + '_'.join(map(str, ids))
|
||||
|
||||
def is_boolean_group(name): return name.startswith('in_group_')
|
||||
def is_boolean_groups(name): return name.startswith('in_groups_')
|
||||
def is_selection_groups(name): return name.startswith('sel_groups_')
|
||||
def is_field_group(name):
|
||||
return is_boolean_group(name) or is_boolean_groups(name) or is_selection_groups(name)
|
||||
|
||||
def get_boolean_group(name): return int(name[9:])
|
||||
def get_boolean_groups(name): return map(int, name[10:].split('_'))
|
||||
def get_selection_groups(name): return map(int, name[11:].split('_'))
|
||||
|
||||
def encode(s): return s.encode('utf8') if isinstance(s, unicode) else s
|
||||
def partition(f, xs):
|
||||
"return a pair equivalent to (filter(f, xs), filter(lambda x: not f(x), xs))"
|
||||
yes, nos = [], []
|
||||
for x in xs:
|
||||
if f(x):
|
||||
yes.append(x)
|
||||
else:
|
||||
nos.append(x)
|
||||
return yes, nos
|
||||
|
||||
class users_view(osv.osv):
|
||||
_inherit = 'res.users'
|
||||
|
||||
def _process_values_groups(self, cr, uid, values, context=None):
|
||||
""" transform all reified group fields into a 'groups_id', adding
|
||||
also the implied groups """
|
||||
add, rem = [], []
|
||||
for k in values.keys():
|
||||
if is_boolean_group(k):
|
||||
if values.pop(k):
|
||||
add.append(get_boolean_group(k))
|
||||
else:
|
||||
rem.append(get_boolean_group(k))
|
||||
elif is_boolean_groups(k):
|
||||
if not values.pop(k):
|
||||
rem.extend(get_boolean_groups(k))
|
||||
elif is_selection_groups(k):
|
||||
gid = values.pop(k)
|
||||
if gid:
|
||||
rem.extend(get_selection_groups(k))
|
||||
add.append(gid)
|
||||
if add or rem:
|
||||
# remove groups in 'rem' and add groups in 'add'
|
||||
gdiff = [(3, id) for id in rem] + [(4, id) for id in add]
|
||||
values.setdefault('groups_id', []).extend(gdiff)
|
||||
return True
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
self._process_values_groups(cr, uid, values, context)
|
||||
return super(users_view, self).create(cr, uid, values, context)
|
||||
|
||||
def write(self, cr, uid, ids, values, context=None):
|
||||
self._process_values_groups(cr, uid, values, context)
|
||||
return super(users_view, self).write(cr, uid, ids, values, context)
|
||||
|
||||
def read(self, cr, uid, ids, fields, context=None, load='_classic_read'):
|
||||
if not fields:
|
||||
group_fields, fields = [], self.fields_get(cr, uid, context).keys()
|
||||
else:
|
||||
group_fields, fields = partition(is_field_group, fields)
|
||||
if group_fields:
|
||||
group_obj = self.pool.get('res.groups')
|
||||
fields.append('groups_id')
|
||||
# read the normal fields (and 'groups_id')
|
||||
res = super(users_view, self).read(cr, uid, ids, fields, context, load)
|
||||
records = res if isinstance(res, list) else [res]
|
||||
for record in records:
|
||||
# get the field 'groups_id' and insert the group_fields
|
||||
groups = set(record['groups_id'])
|
||||
for f in group_fields:
|
||||
if is_boolean_group(f):
|
||||
record[f] = get_boolean_group(f) in groups
|
||||
elif is_boolean_groups(f):
|
||||
record[f] = not groups.isdisjoint(get_boolean_groups(f))
|
||||
elif is_selection_groups(f):
|
||||
selected = groups.intersection(get_selection_groups(f))
|
||||
record[f] = group_obj.get_maximal(cr, uid, selected, context)
|
||||
return res
|
||||
return super(users_view, self).read(cr, uid, ids, fields, context, load)
|
||||
|
||||
def fields_view_get(self, cr, uid, view_id=None, view_type='form',
|
||||
context=None, toolbar=False, submenu=False):
|
||||
# in form views, transform 'groups_id' into reified group fields
|
||||
res = super(users_view, self).fields_view_get(cr, uid, view_id, view_type,
|
||||
context, toolbar, submenu)
|
||||
if view_type == 'form':
|
||||
root = etree.fromstring(encode(res['arch']))
|
||||
nodes = root.xpath("//field[@name='groups_id']")
|
||||
if nodes:
|
||||
# replace node by the reified group fields
|
||||
fields = res['fields']
|
||||
elems = []
|
||||
apps, others = self.pool.get('res.groups').get_classified(cr, uid, context)
|
||||
# create section Applications
|
||||
elems.append('<separator colspan="6" string="%s"/>' % _('Applications'))
|
||||
for app, groups in apps:
|
||||
ids = [g.id for name, g in groups]
|
||||
app_name = name_boolean_groups(ids)
|
||||
sel_name = name_selection_groups(ids)
|
||||
selection = [(g.id, name) for name, g in groups]
|
||||
fields[app_name] = {'type': 'boolean', 'string': app}
|
||||
tips = [name + ': ' + (g.comment or '') for name, g in groups]
|
||||
if tips:
|
||||
fields[app_name].update(help='\n'.join(tips))
|
||||
fields[sel_name] = {'type': 'selection', 'string': 'Group', 'selection': selection}
|
||||
elems.append("""
|
||||
<field name="%(app)s"/>
|
||||
<field name="%(sel)s" nolabel="1" colspan="2"
|
||||
attrs="{'invisible': [('%(app)s', '=', False)]}"
|
||||
modifiers="{'invisible': [('%(app)s', '=', False)]}"/>
|
||||
<newline/>
|
||||
""" % {'app': app_name, 'sel': sel_name})
|
||||
# create other sections
|
||||
for sec, groups in others:
|
||||
elems.append('<separator colspan="6" string="%s"/>' % sec)
|
||||
for gname, g in groups:
|
||||
name = name_boolean_group(g.id)
|
||||
fields[name] = {'type': 'boolean', 'string': gname}
|
||||
if g.comment:
|
||||
fields[name].update(help=g.comment)
|
||||
elems.append('<field name="%s"/>' % name)
|
||||
elems.append('<newline/>')
|
||||
# replace xml node by new arch
|
||||
new_node = etree.fromstring('<group col="6">' + ''.join(elems) + '</group>')
|
||||
for node in nodes:
|
||||
node.getparent().replace(node, new_node)
|
||||
res['arch'] = etree.tostring(root)
|
||||
return res
|
||||
|
||||
users_view()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
<!--
|
||||
Users Groups
|
||||
-->
|
||||
<record model="res.groups" id="group_system">
|
||||
<field name="name">Administration / Configuration</field>
|
||||
</record>
|
||||
|
||||
<record model="res.groups" id="group_erp_manager">
|
||||
<field name="name">Administration / Access Rights</field>
|
||||
</record>
|
||||
<record model="res.groups" id="group_system">
|
||||
<field name="name">Administration / Configuration</field>
|
||||
<field name="implied_ids" eval="[(4, ref('group_erp_manager'))]"/>
|
||||
</record>
|
||||
|
||||
<record model="res.groups" id="group_user">
|
||||
<field name="name">Employee</field>
|
||||
@@ -29,12 +29,13 @@
|
||||
<field name="name">Useability / No One</field>
|
||||
</record>
|
||||
|
||||
<record id="group_sale_manager" context="{'noadmin':True}" model="res.groups">
|
||||
<field name="name">Sales / Manager</field>
|
||||
</record>
|
||||
<record id="group_sale_salesman" context="{'noadmin':True}" model="res.groups">
|
||||
<field name="name">Sales / User</field>
|
||||
</record>
|
||||
<record id="group_sale_manager" context="{'noadmin':True}" model="res.groups">
|
||||
<field name="name">Sales / Manager</field>
|
||||
<field name="implied_ids" eval="[(4, ref('group_sale_salesman'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Set accesses to menu -->
|
||||
<record model="ir.ui.menu" id="base.menu_administration">
|
||||
|
||||
Reference in New Issue
Block a user