From 820135d8acb5cdea5f26bb96a909d8d57115e54f Mon Sep 17 00:00:00 2001 From: "RavishchanraMurari (Open ERP)" Date: Thu, 9 Jun 2011 14:50:59 +0530 Subject: [PATCH 01/43] remove_address_id bzr revid: rmu@tinyerp.com-20110609092059-tf6tn6e40xk3lbnj --- openerp/addons/base/base_update.xml | 6 ++-- openerp/addons/base/res/res_user.py | 50 ++++++++++++++--------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/openerp/addons/base/base_update.xml b/openerp/addons/base/base_update.xml index 7432018fd94..220e9628c4e 100644 --- a/openerp/addons/base/base_update.xml +++ b/openerp/addons/base/base_update.xml @@ -119,7 +119,7 @@ context="{'user_preference': 0}" groups="base.group_multi_company" /> - + @@ -175,7 +175,7 @@ - + @@ -255,7 +255,7 @@ - + diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 2ef2bb8d4d0..ca492c3fe96 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -136,7 +136,7 @@ class users(osv.osv): _('"email_from" needs to be set to send welcome mails ' 'to users')) return False - if not user.get('email'): + if not user.get('user_email'): return False return tools.email_send(email_from=None, email_to=[user['email']], @@ -175,24 +175,24 @@ 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 _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: @@ -217,13 +217,13 @@ 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, + 'user_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."), 'signature': fields.text('Signature', size=64), - 'address_id': fields.many2one('res.partner.address', 'Address'), + # '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."), @@ -245,7 +245,7 @@ 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="Choose between the simplified interface and the extended one"), - 'user_email': fields.function(_email_get, method=True, fnct_inv=_email_set, string='Email', type="char", size=240), + # '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), } @@ -345,7 +345,7 @@ class users(osv.osv): 'company_id': _get_company, 'company_ids': _get_companies, 'groups_id': _get_group, - 'address_id': False, + # 'address_id': False, 'menu_tips':True } @@ -532,13 +532,13 @@ class config_users(osv.osv_memory): partner_id = self.pool.get('res.partner').main_partner(cr, uid) address = self.pool.get('res.partner.address').create( cr, uid, {'name': base_data['name'], - 'email': base_data['email'], + 'user_email': base_data['user_email'], 'partner_id': partner_id,}, context) user_data = dict( base_data, signature=self._generate_signature( - cr, base_data['name'], base_data['email'], context=context), + cr, base_data['name'], base_data['user_email'], context=context), address_id=address, ) new_user = self.pool.get('res.users').create( From 0187c6cf666e35b7541bfee0d6f36e8faa687baa Mon Sep 17 00:00:00 2001 From: "RavishchanraMurari (Open ERP)" Date: Thu, 16 Jun 2011 17:55:21 +0530 Subject: [PATCH 02/43] user address bzr revid: rmu@tinyerp.com-20110616122521-xymw519yxvq6amxt --- openerp/addons/base/res/res_user.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index ca492c3fe96..ab2c56b42d5 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -513,10 +513,10 @@ class config_users(osv.osv_memory): _name = 'res.config.users' _inherit = ['res.users', 'res.config'] - def _generate_signature(self, cr, name, email, context=None): - return _('--\n%(name)s %(email)s\n') % { + def _generate_signature(self, cr, name, user_email, context=None): + return _('--\n%(name)s %(user_email)s\n') % { 'name': name or '', - 'email': email and ' <'+email+'>' or '', + 'user_mail': user_email and ' <'+user_email+'>' or '', } def create_user(self, cr, uid, new_id, context=None): @@ -529,21 +529,23 @@ class config_users(osv.osv_memory): with the user's data %-formatted into the mail body """ base_data = self.read(cr, uid, new_id, context=context) - partner_id = self.pool.get('res.partner').main_partner(cr, uid) - address = self.pool.get('res.partner.address').create( - cr, uid, {'name': base_data['name'], - 'user_email': base_data['user_email'], - 'partner_id': partner_id,}, - context) + #partner_id = self.pool.get('res.partner').main_partner(cr, uid) + #address = self.pool.get('res.partner.address').create( + + #cr, uid, {'name': base_data['name'], + #'email': base_data['email'], + #'partner_id': partner_id,},context) + user_data = dict( base_data, signature=self._generate_signature( - cr, base_data['name'], base_data['user_email'], context=context), - address_id=address, + cr, base_data['name'], base_data['user_email'], context=context) + #address_id=address, ) new_user = self.pool.get('res.users').create( cr, uid, user_data, context) self.send_welcome_email(cr, uid, new_user, context=context) + def execute(self, cr, uid, ids, context=None): 'Do nothing on execution, just launch the next action/todo' pass From 067b91a961698d57ab606afb9766fe6ac0e774c1 Mon Sep 17 00:00:00 2001 From: "RavishchanraMurari (Open ERP)" Date: Fri, 17 Jun 2011 13:29:54 +0530 Subject: [PATCH 03/43] user address bzr revid: rmu@tinyerp.com-20110617075954-tis7gi2fezxjyjd9 --- openerp/addons/base/res/res_user.py | 44 +++++++---------------------- 1 file changed, 10 insertions(+), 34 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index ab2c56b42d5..42e7307aca0 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -113,6 +113,7 @@ class users(osv.osv): is to return config_users.WELCOME_MAIL_SUBJECT """ return self.WELCOME_MAIL_SUBJECT + def get_welcome_mail_body(self, cr, uid, context=None): """ Returns the subject of the mail new users receive (when created via the res.config.users wizard), default implementation @@ -175,24 +176,7 @@ 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: @@ -217,14 +201,15 @@ 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!"), + 'user_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."), + 'signature': fields.text('Signature', size=64), - # 'address_id': fields.many2one('res.partner.address', 'Address'), - 'active': fields.boolean('Active'), + '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."), 'groups_id': fields.many2many('res.groups', 'res_groups_users_rel', 'uid', 'gid', 'Groups'), @@ -245,8 +230,7 @@ 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="Choose between the simplified interface and the extended one"), - # '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"), + '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), } @@ -345,7 +329,6 @@ class users(osv.osv): 'company_id': _get_company, 'company_ids': _get_companies, 'groups_id': _get_group, - # 'address_id': False, 'menu_tips':True } @@ -514,9 +497,9 @@ class config_users(osv.osv_memory): _inherit = ['res.users', 'res.config'] def _generate_signature(self, cr, name, user_email, context=None): - return _('--\n%(name)s %(user_email)s\n') % { + return _('--\n%(name)s %(email)s\n') % { 'name': name or '', - 'user_mail': user_email and ' <'+user_email+'>' or '', + 'email': email and ' <'+email+'>' or '', } def create_user(self, cr, uid, new_id, context=None): @@ -529,18 +512,11 @@ class config_users(osv.osv_memory): with the user's data %-formatted into the mail body """ base_data = self.read(cr, uid, new_id, context=context) - #partner_id = self.pool.get('res.partner').main_partner(cr, uid) - #address = self.pool.get('res.partner.address').create( - - #cr, uid, {'name': base_data['name'], - #'email': base_data['email'], - #'partner_id': partner_id,},context) - + user_data = dict( base_data, signature=self._generate_signature( - cr, base_data['name'], base_data['user_email'], context=context) - #address_id=address, + cr, base_data['name'], base_data['email'], context=context) ) new_user = self.pool.get('res.users').create( cr, uid, user_data, context) From e8b98c450b8ad55cb00b08cb3c4c37f01ec6faa2 Mon Sep 17 00:00:00 2001 From: "RavishchanraMurari (Open ERP)" Date: Tue, 21 Jun 2011 16:07:29 +0530 Subject: [PATCH 04/43] user address bzr revid: rmu@tinyerp.com-20110621103729-6xbkj71rdkfp9q7y --- openerp/addons/base/res/res_user.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 42e7307aca0..c91be997172 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -38,7 +38,7 @@ class groups(osv.osv): 'name': fields.char('Group Name', size=64, required=True), '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)]), + 'group_id', 'rule_group_id', 'Rules', domain=[('global', '=', False)]), 'menu_access': fields.many2many('ir.ui.menu', 'ir_ui_menu_group_rel', 'gid', 'menu_id', 'Access Menu'), 'comment' : fields.text('Comment',size=250), } @@ -113,7 +113,6 @@ class users(osv.osv): is to return config_users.WELCOME_MAIL_SUBJECT """ return self.WELCOME_MAIL_SUBJECT - def get_welcome_mail_body(self, cr, uid, context=None): """ Returns the subject of the mail new users receive (when created via the res.config.users wizard), default implementation @@ -201,13 +200,11 @@ 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!"), - 'user_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."), - 'signature': fields.text('Signature', size=64), '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."), @@ -496,7 +493,7 @@ class config_users(osv.osv_memory): _name = 'res.config.users' _inherit = ['res.users', 'res.config'] - def _generate_signature(self, cr, name, user_email, context=None): + def _generate_signature(self, cr, name, email, context=None): return _('--\n%(name)s %(email)s\n') % { 'name': name or '', 'email': email and ' <'+email+'>' or '', @@ -512,7 +509,6 @@ class config_users(osv.osv_memory): with the user's data %-formatted into the mail body """ base_data = self.read(cr, uid, new_id, context=context) - user_data = dict( base_data, signature=self._generate_signature( From fa645f7f0bc9195886dc15e195721557c631b82b Mon Sep 17 00:00:00 2001 From: "RavishchanraMurari (Open ERP)" Date: Tue, 21 Jun 2011 16:24:36 +0530 Subject: [PATCH 05/43] user address bzr revid: rmu@tinyerp.com-20110621105436-i4us13fk76ntx7gn --- openerp/addons/base/res/res_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index c91be997172..ea1f6b2142e 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -136,7 +136,7 @@ class users(osv.osv): _('"email_from" needs to be set to send welcome mails ' 'to users')) return False - if not user.get('user_email'): + if not user.get('email'): return False return tools.email_send(email_from=None, email_to=[user['email']], From ae6007b9cf9fe0e74dde7f0e29e33d868c2ab1e6 Mon Sep 17 00:00:00 2001 From: "RavishchanraMurari (Open ERP)" Date: Mon, 25 Jul 2011 15:26:16 +0530 Subject: [PATCH 06/43] update_address_id_usr_email bzr revid: rmu@tinyerp.com-20110725095616-5p2lkyomcxyo4t0n --- openerp/addons/base/base_update.xml | 2 -- openerp/addons/base/res/res_user.py | 27 +++------------------------ 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/openerp/addons/base/base_update.xml b/openerp/addons/base/base_update.xml index 1820c8df9f3..ed67b2f16ee 100644 --- a/openerp/addons/base/base_update.xml +++ b/openerp/addons/base/base_update.xml @@ -119,7 +119,6 @@ context="{'user_preference': 0}" groups="base.group_multi_company" /> - @@ -175,7 +174,6 @@ - diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index f3c0f635d14..edeb334d2fb 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -175,25 +175,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 +201,12 @@ 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, + 'user_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."), '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 +227,7 @@ 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 +326,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 +393,7 @@ 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! + # avoid sharing the address of the copied user! ) copydef.update(default) return super(users, self).copy(cr, uid, id, copydef, context) From e0f401bae4f5b2dadfcc279575ca0f2f6d4a60e1 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 26 Jul 2011 12:31:00 +0200 Subject: [PATCH 07/43] [IMP] merged the python classes defining res.groups bzr revid: rco@openerp.com-20110726103100-6ibtqsfnim3unpg3 --- openerp/addons/base/res/res_user.py | 40 ++++++++++++----------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index f3c0f635d14..58793bc638d 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -36,6 +36,7 @@ class groups(osv.osv): _description = "Access Groups" _columns = { 'name': fields.char('Group Name', size=64, required=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 +77,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') @@ -510,30 +526,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' From 398d401724bde7cf0db0072b040c51897c916847 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 27 Jul 2011 15:11:47 +0200 Subject: [PATCH 08/43] [IMP] add an implication relation between groups bzr revid: rco@openerp.com-20110727131147-yiqgzy3sjv3ismvi --- openerp/addons/base/res/res_user.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 58793bc638d..0dd5d011be3 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -526,6 +526,30 @@ class users(osv.osv): users() + + +class groups2(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 implied_groups(self, cr, uid, ids, context=None): + "return all groups recursively implied by the given ids" + closure = set() + todo = self.browse(cr, uid, ids, context) + while todo: + g = todo.pop() + if g.id not in closure: + closure.add(g.id) + todo.extend(g.implied_ids) + return list(closure) + +groups2() + + + class res_config_view(osv.osv_memory): _name = 'res.config.view' _inherit = 'res.config' From 33eef3434934fe6d3384cbab2ff64b784e4de3ba Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 28 Jul 2011 09:25:44 +0200 Subject: [PATCH 09/43] first sketch of groups with boolean fields bzr revid: rco@openerp.com-20110728072544-tyseggysom5y8azw --- openerp/addons/base/res/res_user.py | 82 +++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 0dd5d011be3..540c62ac23d 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -29,6 +29,7 @@ import pooler from tools.translate import _ from service import security import netsvc +from lxml import etree class groups(osv.osv): _name = "res.groups" @@ -535,10 +536,10 @@ class groups2(osv.osv): string='Inherits', help='Users of this group automatically inherit those groups'), } - def implied_groups(self, cr, uid, ids, context=None): - "return all groups recursively implied by the given ids" + def get_closure(self, cr, ids): + "return the closure of ids, i.e., all groups recursively implied by ids" closure = set() - todo = self.browse(cr, uid, ids, context) + todo = self.browse(cr, 1, ids) while todo: g = todo.pop() if g.id not in closure: @@ -550,6 +551,81 @@ groups2() +class users2(osv.osv): + _inherit = 'res.users' + + def values_to_groups_id(self, cr, uid, values, context=None): + """ transform all fields like 'in_group_ID' into a 'groups_id', adding + also the implied groups """ + add, rem = [], [] + for k in values.keys(): + if k.startswith('in_group_'): + # remove k from values, and place it in add or rem + (add if values.pop(k) else rem).append(int(k[9:])) + if add or rem: + # remove groups in 'rem' and add all implied groups in 'add' + add = self.pool.get('res.groups').get_closure(cr, add) + values['groups_id'] = [(3, id) for id in rem] + [(4, id) for id in add] + return True + + def create(self, cr, uid, values, context=None): + # add processing for boolean fields 'in_group_ID' + self.values_to_groups_id(cr, uid, values, context) + return super(users2, self).create(cr, uid, values, context) + + def write(self, cr, uid, ids, values, context=None): + # add processing for boolean fields 'in_group_ID' + self.values_to_groups_id(cr, uid, values, context) + return super(users2, self).write(cr, uid, ids, values, context) + + def read(self, cr, uid, ids, fields, context=None, load='_classic_read'): + # add processing for boolean fields 'in_group_ID' + group_fields = [] + other_fields = [] + for f in fields: + if f.startswith('in_group_'): + group_fields.append(f) + else: + other_fields.append(f) + if group_fields: + fields = other_fields + ['groups_id'] + res = super(users2, self).read(cr, uid, ids, fields, context, load) + for record in res: + # remove the field 'groups_id' and insert the group_fields + groups = record.pop('groups_id') + for f in group_fields: + record[f] = int(f[9:]) in groups + return res + return super(users2, 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 boolean fields 'in_group_ID' + res = super(users2, self).fields_view_get(cr, uid, view_id, view_type, + context, toolbar, submenu) + if view_type == 'form': + root = etree.fromstring(res['arch']) + nodes = root.xpath("//field[@name='groups_id']") + if nodes: + # replace node by the boolean fields 'in_group_ID' + fields = res['fields'] + del fields['groups_id'] + groups = self.pool.get('res.groups') + elems = [] + for g in groups.browse(cr, 1, groups.search(cr, 1, [])): + name = 'in_group_%d' % g.id + fields[name] = {'type': 'boolean', 'string': g.name, 'views': {}} + elems.append('' % name) + new_node = etree.fromstring('' + ''.join(elems) + '') + for node in nodes: + node.getparent().replace(node, new_node) + res['arch'] = etree.tostring(root) + return res + +users2() + + + class res_config_view(osv.osv_memory): _name = 'res.config.view' _inherit = 'res.config' From 0c5ec449bffc59411a46f4c3c03d9eccd8082d77 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 28 Jul 2011 09:29:48 +0200 Subject: [PATCH 10/43] make group names translatable bzr revid: rco@openerp.com-20110728072948-5nkse5obxz739r4r --- openerp/addons/base/res/res_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 540c62ac23d..651a4915e88 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -36,7 +36,7 @@ class groups(osv.osv): _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', From 8a459051d5b3cf69242f11aaad04702328dfa231 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 28 Jul 2011 11:59:58 +0200 Subject: [PATCH 11/43] add method to get groups classified bzr revid: rco@openerp.com-20110728095958-bgplcmlrwbp017nr --- openerp/addons/base/res/res_user.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 651a4915e88..6d53be5955a 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -547,6 +547,27 @@ class groups2(osv.osv): todo.extend(g.implied_ids) return list(closure) + def get_classified(self, cr, uid, context=None): + "return a classification (by application, etc.) of all groups" + # determine the names of apps (that correspond to root menus) + 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 = set([m.name for m in menu_obj.browse(cr, uid, menu_ids, context)]) + + # classify groups depending on their names + groups, others = {}, {} + for g in self.browse(cr, uid, self.search(cr, uid, []), context): + names = [s.strip() for s in g.name.split('/', 1)] + if len(names) > 1: + if names[0] in apps: + groups.setdefault(names[0], []).append((g.id, names[1])) + else: + others.setdefault(names[0], []).append((g.id, names[1])) + else: + others.setdefault(None, []).append((g.id, names[0])) + + return groups, others + groups2() From 5444ec5a227747af016d3afae23eed08d5b51804 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 28 Jul 2011 14:06:54 +0200 Subject: [PATCH 12/43] add method to compute the closure of the relation implied_ids bzr revid: rco@openerp.com-20110728120654-q0tewgjk7qxf6t8h --- openerp/addons/base/res/res_user.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 6d53be5955a..c583848d549 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -547,6 +547,21 @@ class groups2(osv.osv): todo.extend(g.implied_ids) return list(closure) + def get_rec_implied(self, cr): + "return a dictionary giving the recursively implied groups of each group" + groups = self.browse(cr, 1, self.search(cr, 1, [])) + # compute the transitive closure of implied_ids + succs = dict([(g.id, set()) for g in groups]) + preds = dict([(g.id, set()) for g in groups]) + for g in groups: + for h in g.implied_ids: + # link g and its predecessors to h and its successors + ps = preds[g.id].union([g.id]) + ss = succs[h.id].union([h.id]) + for p in ps: succs[p] |= ss + for s in ss: preds[s] |= ps + return succs + def get_classified(self, cr, uid, context=None): "return a classification (by application, etc.) of all groups" # determine the names of apps (that correspond to root menus) From ab63014e156c0b9077b23fb4dd6fa895baf891e3 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 28 Jul 2011 15:10:22 +0200 Subject: [PATCH 13/43] improve group classification bzr revid: rco@openerp.com-20110728131022-1w6t86lnext9e2yg --- openerp/addons/base/res/res_user.py | 42 ++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index c583848d549..4ffe6b0bd51 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -563,26 +563,42 @@ class groups2(osv.osv): return succs def get_classified(self, cr, uid, context=None): - "return a classification (by application, etc.) of all groups" - # determine the names of apps (that correspond to root menus) - 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 = set([m.name for m in menu_obj.browse(cr, uid, menu_ids, context)]) + """ + classify all groups by prefix; return a pair (apps, groups) where + - apps is a list like [("App", [(id, "Name"), ...]), ...], + - groups is a dictionary like {'Class': [(id, "Name"), ...], ...} + - the key None is used in groups for groups not like App/Name + """ + # get the relation to order groups + order_relation = self.get_rec_implied(cr) + order = lambda x, y: (x[0] in order_relation[y[0]] and 1 or -1) # classify groups depending on their names - groups, others = {}, {} + classified = {} for g in self.browse(cr, uid, self.search(cr, uid, []), context): names = [s.strip() for s in g.name.split('/', 1)] if len(names) > 1: - if names[0] in apps: - groups.setdefault(names[0], []).append((g.id, names[1])) - else: - others.setdefault(names[0], []).append((g.id, names[1])) + classified.setdefault(names[0], []).append((g.id, names[1])) else: - others.setdefault(None, []).append((g.id, names[0])) + classified.setdefault(None, []).append((g.id, names[0])) + + # 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: + # sort application groups by implication + groups = classified.pop(m.name) + groups.sort(order) + apps.append((m.name, groups)) + + # sort remaining groups alphabetically in their class + for groups in classified.itervalues(): + groups.sort(key=lambda pair: pair[1]) + + return (apps, classified) - return groups, others - groups2() From 8004cea65f009c29d746e6480511b8e3305e50a9 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 28 Jul 2011 16:51:31 +0200 Subject: [PATCH 14/43] build the view based on classified groups bzr revid: rco@openerp.com-20110728145131-j8myem3vd25j1drf --- openerp/addons/base/res/res_user.py | 94 ++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 21 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 4ffe6b0bd51..fdd8fc7302e 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -603,17 +603,42 @@ groups2() +# 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 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('_')) + class users2(osv.osv): _inherit = 'res.users' def values_to_groups_id(self, cr, uid, values, context=None): - """ transform all fields like 'in_group_ID' into a 'groups_id', adding + """ transform all reified group fields into a 'groups_id', adding also the implied groups """ add, rem = [], [] for k in values.keys(): - if k.startswith('in_group_'): - # remove k from values, and place it in add or rem - (add if values.pop(k) else rem).append(int(k[9:])) + if is_boolean_group(k): + (add if values.pop(k) else rem).append(get_boolean_group(k)) + elif is_boolean_groups(k): + if not values.pop(k): + rem.extend(get_selection_groups(k)) + elif is_selection_groups(k): + rem.extend(get_selection_groups(k)) + add.append(int(values.pop(k))) if add or rem: # remove groups in 'rem' and add all implied groups in 'add' add = self.pool.get('res.groups').get_closure(cr, add) @@ -621,21 +646,20 @@ class users2(osv.osv): return True def create(self, cr, uid, values, context=None): - # add processing for boolean fields 'in_group_ID' + # add processing for reified group fields self.values_to_groups_id(cr, uid, values, context) return super(users2, self).create(cr, uid, values, context) def write(self, cr, uid, ids, values, context=None): - # add processing for boolean fields 'in_group_ID' + # add processing for reified group fields self.values_to_groups_id(cr, uid, values, context) return super(users2, self).write(cr, uid, ids, values, context) def read(self, cr, uid, ids, fields, context=None, load='_classic_read'): - # add processing for boolean fields 'in_group_ID' - group_fields = [] - other_fields = [] + # add processing for reified group fields + group_fields, other_fields = [], [] for f in fields: - if f.startswith('in_group_'): + if is_boolean_group(f) or is_boolean_groups(f) or is_selection_groups(f): group_fields.append(f) else: other_fields.append(f) @@ -644,31 +668,59 @@ class users2(osv.osv): res = super(users2, self).read(cr, uid, ids, fields, context, load) for record in res: # remove the field 'groups_id' and insert the group_fields - groups = record.pop('groups_id') + groups = set(record.pop('groups_id')) for f in group_fields: - record[f] = int(f[9:]) in groups + 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): + record[f] = False + for gid in get_selection_groups(f): + if gid in groups: + record[f] = gid + break return res return super(users2, 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 boolean fields 'in_group_ID' + # in form views, transform 'groups_id' into reified group fields res = super(users2, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu) if view_type == 'form': root = etree.fromstring(res['arch']) nodes = root.xpath("//field[@name='groups_id']") if nodes: - # replace node by the boolean fields 'in_group_ID' + # replace node by the reified group fields fields = res['fields'] - del fields['groups_id'] - groups = self.pool.get('res.groups') elems = [] - for g in groups.browse(cr, 1, groups.search(cr, 1, [])): - name = 'in_group_%d' % g.id - fields[name] = {'type': 'boolean', 'string': g.name, 'views': {}} - elems.append('' % name) - new_node = etree.fromstring('' + ''.join(elems) + '') + apps, others = self.pool.get('res.groups').get_classified(cr, uid, context) + # create section Applications + elems.append('' % _('Applications')) + for app, selection in apps: + ids = [id for id, name in selection] + app_name = name_boolean_groups(ids) + sel_name = name_selection_groups(ids) + fields[app_name] = {'type': 'boolean', 'string': app} + fields[sel_name] = {'type': 'selection', 'string': '', 'selection': selection} + elems.append('' % app_name) + elems.append('' % sel_name) + elems.append('') + # create other sections + sections = sorted(others.items(), key=lambda pair: pair[0]) + if sections and sections[0][0] is None: + sec, selection = sections.pop(0) + sections.append((_('Others'), selection)) + for sec, selection in sections: + elems.append('' % sec) + for id, gname in selection: + name = name_boolean_group(id) + fields[name] = {'type': 'boolean', 'string': gname} + elems.append('' % name) + elems.append('') + # replace xml node by new arch + new_node = etree.fromstring('' + ''.join(elems) + '') for node in nodes: node.getparent().replace(node, new_node) res['arch'] = etree.tostring(root) From 82ba03cf9d33ecfce21afb06be2f7081d51840c1 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 09:40:25 +0200 Subject: [PATCH 15/43] fix fields_view_get, and hide selection field when app not selected bzr revid: rco@openerp.com-20110729074025-ugr2rkm1mxnwle1s --- openerp/addons/base/res/res_user.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index fdd8fc7302e..f8c5c034f61 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -623,6 +623,8 @@ 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 + class users2(osv.osv): _inherit = 'res.users' @@ -635,7 +637,7 @@ class users2(osv.osv): (add if values.pop(k) else rem).append(get_boolean_group(k)) elif is_boolean_groups(k): if not values.pop(k): - rem.extend(get_selection_groups(k)) + rem.extend(get_boolean_groups(k)) elif is_selection_groups(k): rem.extend(get_selection_groups(k)) add.append(int(values.pop(k))) @@ -689,7 +691,7 @@ class users2(osv.osv): res = super(users2, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu) if view_type == 'form': - root = etree.fromstring(res['arch']) + root = etree.fromstring(encode(res['arch'])) nodes = root.xpath("//field[@name='groups_id']") if nodes: # replace node by the reified group fields @@ -698,6 +700,7 @@ class users2(osv.osv): apps, others = self.pool.get('res.groups').get_classified(cr, uid, context) # create section Applications elems.append('' % _('Applications')) + elems.append('') for app, selection in apps: ids = [id for id, name in selection] app_name = name_boolean_groups(ids) @@ -705,8 +708,9 @@ class users2(osv.osv): fields[app_name] = {'type': 'boolean', 'string': app} fields[sel_name] = {'type': 'selection', 'string': '', 'selection': selection} elems.append('' % app_name) - elems.append('' % sel_name) + elems.append("""""" % (sel_name, app_name)) elems.append('') + elems.append('') # create other sections sections = sorted(others.items(), key=lambda pair: pair[0]) if sections and sections[0][0] is None: From 75a63f5eb43213b356e6a2608c2988dfda953a7a Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 10:41:37 +0200 Subject: [PATCH 16/43] improve group selection fields bzr revid: rco@openerp.com-20110729084137-da89gorw9udmv8i0 --- openerp/addons/base/res/res_user.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index f8c5c034f61..0777070c99c 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -562,6 +562,15 @@ class groups2(osv.osv): for s in ss: preds[s] |= ps return succs + def get_maximal(self, cr, ids): + "return a maximal element among the group ids" + trans_implied = self.get_rec_implied(cr) + res = None + for gid in ids: + if (not res) or (res in trans_implied[gid]): + res = gid + return res + def get_classified(self, cr, uid, context=None): """ classify all groups by prefix; return a pair (apps, groups) where @@ -571,7 +580,7 @@ class groups2(osv.osv): """ # get the relation to order groups order_relation = self.get_rec_implied(cr) - order = lambda x, y: (x[0] in order_relation[y[0]] and 1 or -1) + order = lambda x, y: (x[0] in order_relation[y[0]] and -1 or 1) # classify groups depending on their names classified = {} @@ -666,6 +675,7 @@ class users2(osv.osv): else: other_fields.append(f) if group_fields: + group_obj = self.pool.get('res.groups') fields = other_fields + ['groups_id'] res = super(users2, self).read(cr, uid, ids, fields, context, load) for record in res: @@ -677,11 +687,8 @@ class users2(osv.osv): elif is_boolean_groups(f): record[f] = not groups.isdisjoint(get_boolean_groups(f)) elif is_selection_groups(f): - record[f] = False - for gid in get_selection_groups(f): - if gid in groups: - record[f] = gid - break + selected = groups.intersection(get_selection_groups(f)) + record[f] = group_obj.get_maximal(cr, selected) return res return super(users2, self).read(cr, uid, ids, fields, context, load) @@ -708,7 +715,7 @@ class users2(osv.osv): fields[app_name] = {'type': 'boolean', 'string': app} fields[sel_name] = {'type': 'selection', 'string': '', 'selection': selection} elems.append('' % app_name) - elems.append("""""" % (sel_name, app_name)) + elems.append("""""" % (sel_name, app_name)) elems.append('') elems.append('') # create other sections From 4fec9595c8f919459e51af9733d95366a77f87e8 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 11:46:43 +0200 Subject: [PATCH 17/43] add function partition bzr revid: rco@openerp.com-20110729094643-j0ky9qu3o3w2n1wr --- openerp/addons/base/res/res_user.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 0777070c99c..c696b6ec41a 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -530,6 +530,10 @@ users() class groups2(osv.osv): + """ Extension of res.groups with a relation for implied groups. + The data defines a view that inherits from the user form; that view is + updated when groups are modified, to reflect their structure. + """ _inherit = 'res.groups' _columns = { 'implied_ids': fields.many2many('res.groups', 'res_groups_implied_rel', 'gid', 'hid', @@ -572,8 +576,7 @@ class groups2(osv.osv): return res def get_classified(self, cr, uid, context=None): - """ - classify all groups by prefix; return a pair (apps, groups) where + """ classify all groups by prefix; return a pair (apps, groups) where - apps is a list like [("App", [(id, "Name"), ...]), ...], - groups is a dictionary like {'Class': [(id, "Name"), ...], ...} - the key None is used in groups for groups not like App/Name @@ -627,6 +630,8 @@ 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('_')) @@ -668,12 +673,7 @@ class users2(osv.osv): def read(self, cr, uid, ids, fields, context=None, load='_classic_read'): # add processing for reified group fields - group_fields, other_fields = [], [] - for f in fields: - if is_boolean_group(f) or is_boolean_groups(f) or is_selection_groups(f): - group_fields.append(f) - else: - other_fields.append(f) + group_fields, other_fields = partition(is_field_group, fields) if group_fields: group_obj = self.pool.get('res.groups') fields = other_fields + ['groups_id'] @@ -761,4 +761,16 @@ class res_config_view(osv.osv_memory): res_config_view() + + +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 + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From a7c5e26646344e73e658ba62a67e15593061cd2b Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 12:07:14 +0200 Subject: [PATCH 18/43] small code improvements bzr revid: rco@openerp.com-20110729100714-apwfhm3ee72fkmg9 --- openerp/addons/base/res/res_user.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index c696b6ec41a..0d0023c9694 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -642,7 +642,7 @@ def encode(s): return s.encode('utf8') if isinstance(s, unicode) else s class users2(osv.osv): _inherit = 'res.users' - def values_to_groups_id(self, cr, uid, values, context=None): + def _values_to_groups_id(self, cr, uid, values, context=None): """ transform all reified group fields into a 'groups_id', adding also the implied groups """ add, rem = [], [] @@ -663,20 +663,20 @@ class users2(osv.osv): def create(self, cr, uid, values, context=None): # add processing for reified group fields - self.values_to_groups_id(cr, uid, values, context) + self._values_to_groups_id(cr, uid, values, context) return super(users2, self).create(cr, uid, values, context) def write(self, cr, uid, ids, values, context=None): # add processing for reified group fields - self.values_to_groups_id(cr, uid, values, context) + self._values_to_groups_id(cr, uid, values, context) return super(users2, self).write(cr, uid, ids, values, context) def read(self, cr, uid, ids, fields, context=None, load='_classic_read'): # add processing for reified group fields - group_fields, other_fields = partition(is_field_group, fields) + group_fields, fields = partition(is_field_group, fields) if group_fields: group_obj = self.pool.get('res.groups') - fields = other_fields + ['groups_id'] + fields.append('groups_id') res = super(users2, self).read(cr, uid, ids, fields, context, load) for record in res: # remove the field 'groups_id' and insert the group_fields From 3edcb0e8f3f8d5e71169104add4d76adaaa08fd4 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 12:12:38 +0200 Subject: [PATCH 19/43] small code improvements bzr revid: rco@openerp.com-20110729101238-udkcecldiy9ybfwq --- openerp/addons/base/res/res_user.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 0d0023c9694..9f8cb7d889c 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -714,9 +714,13 @@ class users2(osv.osv): sel_name = name_selection_groups(ids) fields[app_name] = {'type': 'boolean', 'string': app} fields[sel_name] = {'type': 'selection', 'string': '', 'selection': selection} - elems.append('' % app_name) - elems.append("""""" % (sel_name, app_name)) - elems.append('') + elems.append(""" + + + + """ % {'app': app_name, 'sel': sel_name}) elems.append('') # create other sections sections = sorted(others.items(), key=lambda pair: pair[0]) From 7fb84245e972d98540044e88f9a77482f7645f64 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 12:16:20 +0200 Subject: [PATCH 20/43] add comments and small code improvements bzr revid: rco@openerp.com-20110729101620-iknnq7vcchy9mlrr --- openerp/addons/base/res/res_user.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 9f8cb7d889c..8eb94ccfc0e 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -638,8 +638,21 @@ 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 users2(osv.osv): + """ Extension of res.users for: + - adding implied groups when added to a group + - defining special widgets for field groups_id + """ _inherit = 'res.users' def _values_to_groups_id(self, cr, uid, values, context=None): @@ -713,7 +726,7 @@ class users2(osv.osv): app_name = name_boolean_groups(ids) sel_name = name_selection_groups(ids) fields[app_name] = {'type': 'boolean', 'string': app} - fields[sel_name] = {'type': 'selection', 'string': '', 'selection': selection} + fields[sel_name] = {'type': 'selection', 'string': 'Group', 'selection': selection} elems.append(""" Date: Fri, 29 Jul 2011 12:27:46 +0200 Subject: [PATCH 21/43] update group and user views bzr revid: rco@openerp.com-20110729102746-nqah2jqynqiz2lo5 --- openerp/addons/base/base_update.xml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openerp/addons/base/base_update.xml b/openerp/addons/base/base_update.xml index 1820c8df9f3..86b67b56060 100644 --- a/openerp/addons/base/base_update.xml +++ b/openerp/addons/base/base_update.xml @@ -24,6 +24,10 @@ + + @@ -141,10 +145,9 @@ - - - - + + + From 8084d00780e67b7327121f50da6e74c29266cb39 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 13:03:32 +0200 Subject: [PATCH 22/43] improve API of some methods bzr revid: rco@openerp.com-20110729110332-zv5agzip0sq2rwkr --- openerp/addons/base/res/res_user.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 8eb94ccfc0e..17c511c6594 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -540,7 +540,7 @@ class groups2(osv.osv): string='Inherits', help='Users of this group automatically inherit those groups'), } - def get_closure(self, cr, ids): + 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) @@ -551,7 +551,7 @@ class groups2(osv.osv): todo.extend(g.implied_ids) return list(closure) - def get_rec_implied(self, cr): + def get_rec_implied(self, cr, uid, context=None): "return a dictionary giving the recursively implied groups of each group" groups = self.browse(cr, 1, self.search(cr, 1, [])) # compute the transitive closure of implied_ids @@ -566,9 +566,9 @@ class groups2(osv.osv): for s in ss: preds[s] |= ps return succs - def get_maximal(self, cr, ids): + def get_maximal(self, cr, uid, ids, context=None): "return a maximal element among the group ids" - trans_implied = self.get_rec_implied(cr) + trans_implied = self.get_rec_implied(cr, uid, context) res = None for gid in ids: if (not res) or (res in trans_implied[gid]): @@ -582,7 +582,7 @@ class groups2(osv.osv): - the key None is used in groups for groups not like App/Name """ # get the relation to order groups - order_relation = self.get_rec_implied(cr) + order_relation = self.get_rec_implied(cr, uid, context) order = lambda x, y: (x[0] in order_relation[y[0]] and -1 or 1) # classify groups depending on their names @@ -655,7 +655,7 @@ class users2(osv.osv): """ _inherit = 'res.users' - def _values_to_groups_id(self, cr, uid, values, context=None): + 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 = [], [] @@ -670,18 +670,18 @@ class users2(osv.osv): add.append(int(values.pop(k))) if add or rem: # remove groups in 'rem' and add all implied groups in 'add' - add = self.pool.get('res.groups').get_closure(cr, add) + add = self.pool.get('res.groups').get_closure(cr, uid, add, context) values['groups_id'] = [(3, id) for id in rem] + [(4, id) for id in add] return True def create(self, cr, uid, values, context=None): # add processing for reified group fields - self._values_to_groups_id(cr, uid, values, context) + self._process_values_groups(cr, uid, values, context) return super(users2, self).create(cr, uid, values, context) def write(self, cr, uid, ids, values, context=None): # add processing for reified group fields - self._values_to_groups_id(cr, uid, values, context) + self._process_values_groups(cr, uid, values, context) return super(users2, self).write(cr, uid, ids, values, context) def read(self, cr, uid, ids, fields, context=None, load='_classic_read'): @@ -701,7 +701,7 @@ class users2(osv.osv): 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, selected) + record[f] = group_obj.get_maximal(cr, uid, selected, context) return res return super(users2, self).read(cr, uid, ids, fields, context, load) From 2633848b3189f6b1d5fb2a9ae5da336755707b49 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 14:42:35 +0200 Subject: [PATCH 23/43] add implied groups in all cases bzr revid: rco@openerp.com-20110729124235-95a3igb3cmunwiz1 --- openerp/addons/base/res/res_user.py | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 17c511c6594..48b06b172c7 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -551,6 +551,26 @@ class groups2(osv.osv): todo.extend(g.implied_ids) return list(closure) + def create(self, cr, uid, values, context=None): + group_id = super(groups2, self).create(cr, uid, values, context) + if values.get('users') or values.get('implied_ids'): + # add implied groups to all users of the group + group = self.browse(cr, uid, group_id) + self.pool.get('res.users').write(cr, uid, map(int, group.users), + {'groups_id': [(4, group_id)]}, context) + return group_id + + def write(self, cr, uid, ids, values, context=None): + res = super(groups2, 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) + users_obj = self.pool.get('res.users') + groups = self.browse(cr, uid, ids) + for g in groups: + users_obj.write(cr, uid, map(int, g.users), + {'groups_id': [(4, g.id)]}, context) + return res + def get_rec_implied(self, cr, uid, context=None): "return a dictionary giving the recursively implied groups of each group" groups = self.browse(cr, 1, self.search(cr, 1, [])) @@ -672,6 +692,18 @@ class users2(osv.osv): # remove groups in 'rem' and add all implied groups in 'add' add = self.pool.get('res.groups').get_closure(cr, uid, add, context) values['groups_id'] = [(3, id) for id in rem] + [(4, id) for id in add] + elif 'groups_id' in values: + # add implied groups (only handles (4, ID) and (6, 0, IDs) cases) + closure = lambda ids: self.pool.get('res.groups').get_closure(cr, uid, ids, context) + groups = [] + for elem in values['groups_id']: + if elem[0] == 4: + groups.extend([(4, id) for id in closure([elem[1]])]) + elif elem[0] == 6: + groups.append((6, 0, closure(elem[2]))) + else: + groups.append(elem) + values['groups_id'] = groups return True def create(self, cr, uid, values, context=None): From 97dd3f3f414267d779a5b770bcad92987307f35f Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 14:44:51 +0200 Subject: [PATCH 24/43] move code around bzr revid: rco@openerp.com-20110729124451-8c4txfklkoflc2c1 --- openerp/addons/base/res/res_user.py | 40 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 48b06b172c7..cc7b3e47ce6 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -527,6 +527,26 @@ class users(osv.osv): users() +class res_config_view(osv.osv_memory): + _name = 'res.config.view' + _inherit = 'res.config' + _columns = { + 'name':fields.char('Name', size=64), + 'view': fields.selection([('simple','Simplified'), + ('extended','Extended')], + 'Interface', required=True ), + } + _defaults={ + 'view':lambda self,cr,uid,*args: self.pool.get('res.users').browse(cr, uid, uid).view or 'simple', + } + + def execute(self, cr, uid, ids, context=None): + res = self.read(cr, uid, ids)[0] + self.pool.get('res.users').write(cr, uid, [uid], + {'view':res['view']}, context=context) + +res_config_view() + class groups2(osv.osv): @@ -790,24 +810,4 @@ users2() -class res_config_view(osv.osv_memory): - _name = 'res.config.view' - _inherit = 'res.config' - _columns = { - 'name':fields.char('Name', size=64), - 'view': fields.selection([('simple','Simplified'), - ('extended','Extended')], - 'Interface', required=True ), - } - _defaults={ - 'view':lambda self,cr,uid,*args: self.pool.get('res.users').browse(cr, uid, uid).view or 'simple', - } - - def execute(self, cr, uid, ids, context=None): - res = self.read(cr, uid, ids)[0] - self.pool.get('res.users').write(cr, uid, [uid], - {'view':res['view']}, context=context) - -res_config_view() - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From beaf6ec43cef0745dc7218d237f639f1d34bbb89 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 16:04:00 +0200 Subject: [PATCH 25/43] split code in two parts: the implied groups, and enhanced view bzr revid: rco@openerp.com-20110729140400-agr3x0o7xpqxyt0p --- openerp/addons/base/res/res_user.py | 142 ++++++++++++++++------------ 1 file changed, 81 insertions(+), 61 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index cc7b3e47ce6..f903f7bfc4a 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -549,11 +549,13 @@ res_config_view() -class groups2(osv.osv): - """ Extension of res.groups with a relation for implied groups. - The data defines a view that inherits from the user form; that view is - updated when groups are modified, to reflect their structure. - """ +# +# 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', @@ -572,27 +574,25 @@ class groups2(osv.osv): return list(closure) def create(self, cr, uid, values, context=None): - group_id = super(groups2, self).create(cr, uid, values, context) - if values.get('users') or values.get('implied_ids'): - # add implied groups to all users of the group - group = self.browse(cr, uid, group_id) - self.pool.get('res.users').write(cr, uid, map(int, group.users), - {'groups_id': [(4, group_id)]}, context) - return group_id + users = values.pop('users') + 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(groups2, self).write(cr, uid, ids, values, context) + 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) - users_obj = self.pool.get('res.users') - groups = self.browse(cr, uid, ids) - for g in groups: - users_obj.write(cr, uid, map(int, g.users), - {'groups_id': [(4, g.id)]}, context) + 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_rec_implied(self, cr, uid, context=None): - "return a dictionary giving the recursively implied groups of each group" + def get_trans_implied(self, cr, uid, context=None): + "return a dictionary giving the transitively implied groups of each group" groups = self.browse(cr, 1, self.search(cr, 1, [])) # compute the transitive closure of implied_ids succs = dict([(g.id, set()) for g in groups]) @@ -608,21 +608,63 @@ class groups2(osv.osv): def get_maximal(self, cr, uid, ids, context=None): "return a maximal element among the group ids" - trans_implied = self.get_rec_implied(cr, uid, context) res = None for gid in ids: - if (not res) or (res in trans_implied[gid]): + if (not res) or (res in self.get_closure(cr, uid, [gid], context)): res = gid return res +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): + 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): + gids = groups_obj.get_closure(cr, uid, map(int, u.groups_id), context) + groups = [(6, 0, gids)] + super(users_implied, self).write(cr, uid, [u.id], {'groups_id': groups}, 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, groups) where + """ classify all groups by prefix; return a pair (apps, others) where - apps is a list like [("App", [(id, "Name"), ...]), ...], - - groups is a dictionary like {'Class': [(id, "Name"), ...], ...} + - others is a dictionary like {'Class': [(id, "Name"), ...], ...} - the key None is used in groups for groups not like App/Name """ # get the relation to order groups - order_relation = self.get_rec_implied(cr, uid, context) + order_relation = self.get_trans_implied(cr, uid, context) order = lambda x, y: (x[0] in order_relation[y[0]] and -1 or 1) # classify groups depending on their names @@ -650,10 +692,8 @@ class groups2(osv.osv): groups.sort(key=lambda pair: pair[1]) return (apps, classified) - -groups2() - +groups_view() # Naming conventions for reified groups fields: # - boolean field 'in_group_ID' is True iff @@ -688,11 +728,7 @@ def partition(f, xs): nos.append(x) return yes, nos -class users2(osv.osv): - """ Extension of res.users for: - - adding implied groups when added to a group - - defining special widgets for field groups_id - """ +class users_view(osv.osv): _inherit = 'res.users' def _process_values_groups(self, cr, uid, values, context=None): @@ -709,43 +745,29 @@ class users2(osv.osv): rem.extend(get_selection_groups(k)) add.append(int(values.pop(k))) if add or rem: - # remove groups in 'rem' and add all implied groups in 'add' - add = self.pool.get('res.groups').get_closure(cr, uid, add, context) - values['groups_id'] = [(3, id) for id in rem] + [(4, id) for id in add] - elif 'groups_id' in values: - # add implied groups (only handles (4, ID) and (6, 0, IDs) cases) - closure = lambda ids: self.pool.get('res.groups').get_closure(cr, uid, ids, context) - groups = [] - for elem in values['groups_id']: - if elem[0] == 4: - groups.extend([(4, id) for id in closure([elem[1]])]) - elif elem[0] == 6: - groups.append((6, 0, closure(elem[2]))) - else: - groups.append(elem) - values['groups_id'] = groups + # 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): - # add processing for reified group fields self._process_values_groups(cr, uid, values, context) - return super(users2, self).create(cr, uid, values, context) + return super(users_view, self).create(cr, uid, values, context) def write(self, cr, uid, ids, values, context=None): - # add processing for reified group fields self._process_values_groups(cr, uid, values, context) - return super(users2, self).write(cr, uid, ids, 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'): - # add processing for reified group fields group_fields, fields = partition(is_field_group, fields) if group_fields: group_obj = self.pool.get('res.groups') fields.append('groups_id') - res = super(users2, self).read(cr, uid, ids, fields, context, load) + # read the normal fields (and 'groups_id') + res = super(users_view, self).read(cr, uid, ids, fields, context, load) for record in res: - # remove the field 'groups_id' and insert the group_fields - groups = set(record.pop('groups_id')) + # 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 @@ -755,12 +777,12 @@ class users2(osv.osv): selected = groups.intersection(get_selection_groups(f)) record[f] = group_obj.get_maximal(cr, uid, selected, context) return res - return super(users2, self).read(cr, uid, ids, fields, context, load) + 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(users2, self).fields_view_get(cr, uid, view_id, view_type, + 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'])) @@ -806,8 +828,6 @@ class users2(osv.osv): res['arch'] = etree.tostring(root) return res -users2() - - +users_view() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From dca1812f7f3022b31bdaaa62e33c7cef3a469240 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 17:12:16 +0200 Subject: [PATCH 26/43] simple optimization when modifying groups of users bzr revid: rco@openerp.com-20110729151216-7spi02jeibzl7jhz --- openerp/addons/base/res/res_user.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index f903f7bfc4a..0247525616e 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -633,9 +633,11 @@ class users_implied(osv.osv): # add implied groups for all users groups_obj = self.pool.get('res.groups') for u in self.browse(cr, uid, ids): - gids = groups_obj.get_closure(cr, uid, map(int, u.groups_id), context) - groups = [(6, 0, gids)] - super(users_implied, self).write(cr, uid, [u.id], {'groups_id': groups}, context) + 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() From 90d202a4c9d6fa4ea9acde9c4587dccd901b6b88 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 29 Jul 2011 17:15:24 +0200 Subject: [PATCH 27/43] fix methods read and write in users_view bzr revid: rco@openerp.com-20110729151524-3szt56d6icc7j8pn --- openerp/addons/base/res/res_user.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 0247525616e..a18dddbbcb4 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -739,13 +739,18 @@ class users_view(osv.osv): add, rem = [], [] for k in values.keys(): if is_boolean_group(k): - (add if values.pop(k) else rem).append(get_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): - rem.extend(get_selection_groups(k)) - add.append(int(values.pop(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] @@ -767,7 +772,8 @@ class users_view(osv.osv): fields.append('groups_id') # read the normal fields (and 'groups_id') res = super(users_view, self).read(cr, uid, ids, fields, context, load) - for record in res: + 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: From 31131ed5cbecf749cee16d9e9e04a34e22636f5d Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 1 Aug 2011 16:07:03 +0200 Subject: [PATCH 28/43] [IMP] classification of groups bzr revid: rco@openerp.com-20110801140703-5odcxym9xnudffym --- openerp/addons/base/res/res_user.py | 56 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index a18dddbbcb4..1f1ef8f8698 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -661,22 +661,22 @@ class groups_view(osv.osv): def get_classified(self, cr, uid, context=None): """ classify all groups by prefix; return a pair (apps, others) where - - apps is a list like [("App", [(id, "Name"), ...]), ...], - - others is a dictionary like {'Class': [(id, "Name"), ...], ...} - - the key None is used in groups for groups not like App/Name + - 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') """ - # get the relation to order groups - order_relation = self.get_trans_implied(cr, uid, context) - order = lambda x, y: (x[0] in order_relation[y[0]] and -1 or 1) + # sort groups by implication, with implied groups first + trans_implied = self.get_trans_implied(cr, uid, context) + groups = self.browse(cr, uid, self.search(cr, uid, []), context) + groups.sort(key=lambda g: set([g.id]) | trans_implied[g.id]) # classify groups depending on their names classified = {} - for g in self.browse(cr, uid, self.search(cr, uid, []), context): - names = [s.strip() for s in g.name.split('/', 1)] - if len(names) > 1: - classified.setdefault(names[0], []).append((g.id, names[1])) - else: - classified.setdefault(None, []).append((g.id, names[0])) + 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') @@ -684,16 +684,17 @@ class groups_view(osv.osv): apps = [] for m in menu_obj.browse(cr, uid, menu_ids, context): if m.name in classified: - # sort application groups by implication - groups = classified.pop(m.name) - groups.sort(order) - apps.append((m.name, groups)) + # application groups are already sorted by implication + apps.append((m.name, classified.pop(m.name))) - # sort remaining groups alphabetically in their class - for groups in classified.itervalues(): - groups.sort(key=lambda pair: pair[1]) + # 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, classified) + return (apps, others) groups_view() @@ -803,10 +804,11 @@ class users_view(osv.osv): # create section Applications elems.append('' % _('Applications')) elems.append('') - for app, selection in apps: - ids = [id for id, name in selection] + 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} fields[sel_name] = {'type': 'selection', 'string': 'Group', 'selection': selection} elems.append(""" @@ -818,14 +820,10 @@ class users_view(osv.osv): """ % {'app': app_name, 'sel': sel_name}) elems.append('') # create other sections - sections = sorted(others.items(), key=lambda pair: pair[0]) - if sections and sections[0][0] is None: - sec, selection = sections.pop(0) - sections.append((_('Others'), selection)) - for sec, selection in sections: + for sec, groups in others: elems.append('' % sec) - for id, gname in selection: - name = name_boolean_group(id) + for gname, g in groups: + name = name_boolean_group(g.id) fields[name] = {'type': 'boolean', 'string': gname} elems.append('' % name) elems.append('') From 50a4801ff09366607149d7930d0e8b815433d788 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 1 Aug 2011 16:17:22 +0200 Subject: [PATCH 29/43] [IMP] add tooltip for groups bzr revid: rco@openerp.com-20110801141722-50lbhy56zt6ppsng --- openerp/addons/base/res/res_user.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 1f1ef8f8698..0e776c0a392 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -810,6 +810,9 @@ class users_view(osv.osv): 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(""" @@ -825,6 +828,8 @@ class users_view(osv.osv): 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('' % name) elems.append('') # replace xml node by new arch From 8faed1a08a05a6f84b376c57331d645e823c8e1e Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 1 Aug 2011 16:26:07 +0200 Subject: [PATCH 30/43] slight improvement of layout bzr revid: rco@openerp.com-20110801142607-ie3lqx5j4u77wbbn --- openerp/addons/base/res/res_user.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 0e776c0a392..07d8783a138 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -803,7 +803,6 @@ class users_view(osv.osv): apps, others = self.pool.get('res.groups').get_classified(cr, uid, context) # create section Applications elems.append('' % _('Applications')) - elems.append('') for app, groups in apps: ids = [g.id for name, g in groups] app_name = name_boolean_groups(ids) @@ -816,12 +815,11 @@ class users_view(osv.osv): fields[sel_name] = {'type': 'selection', 'string': 'Group', 'selection': selection} elems.append(""" - """ % {'app': app_name, 'sel': sel_name}) - elems.append('') # create other sections for sec, groups in others: elems.append('' % sec) From a3a96878b91c68e5d7c2ba3ebf154571c1db8a46 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 1 Aug 2011 16:51:43 +0200 Subject: [PATCH 31/43] [FIX] some incomplete behaviors bzr revid: rco@openerp.com-20110801145143-sbxda31bazobej6k --- openerp/addons/base/res/res_user.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index 07d8783a138..e3a996412be 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -574,7 +574,7 @@ class groups_implied(osv.osv): return list(closure) def create(self, cr, uid, values, context=None): - users = values.pop('users') + 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 @@ -767,7 +767,10 @@ class users_view(osv.osv): return super(users_view, self).write(cr, uid, ids, values, context) def read(self, cr, uid, ids, fields, context=None, load='_classic_read'): - group_fields, fields = partition(is_field_group, fields) + 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') From 4d79d7efc32269b42cc9ffc4eaa532d00d762c7f Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 2 Aug 2011 14:12:53 +0200 Subject: [PATCH 32/43] [IMP] get maximal group in set bzr revid: rco@openerp.com-20110802121253-hpb7s3pi8eiofz2u --- openerp/addons/base/res/res_user.py | 35 +++++++++++------------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index e3a996412be..6e6b8f03e3f 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -29,6 +29,7 @@ import pooler from tools.translate import _ from service import security import netsvc +import logging from lxml import etree class groups(osv.osv): @@ -591,28 +592,19 @@ class groups_implied(osv.osv): super(groups_implied, self).write(cr, uid, gids, {'users': users}, context) return res - def get_trans_implied(self, cr, uid, context=None): - "return a dictionary giving the transitively implied groups of each group" - groups = self.browse(cr, 1, self.search(cr, 1, [])) - # compute the transitive closure of implied_ids - succs = dict([(g.id, set()) for g in groups]) - preds = dict([(g.id, set()) for g in groups]) - for g in groups: - for h in g.implied_ids: - # link g and its predecessors to h and its successors - ps = preds[g.id].union([g.id]) - ss = succs[h.id].union([h.id]) - for p in ps: succs[p] |= ss - for s in ss: preds[s] |= ps - return succs - def get_maximal(self, cr, uid, ids, context=None): - "return a maximal element among the group ids" - res = None + "return the maximal element among the group ids" + max_set, max_closure = set(), set() for gid in ids: - if (not res) or (res in self.get_closure(cr, uid, [gid], context)): - res = gid - return res + 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() @@ -667,9 +659,8 @@ class groups_view(osv.osv): - groups not like App/Name are at the end of others, under _('Others') """ # sort groups by implication, with implied groups first - trans_implied = self.get_trans_implied(cr, uid, context) groups = self.browse(cr, uid, self.search(cr, uid, []), context) - groups.sort(key=lambda g: set([g.id]) | trans_implied[g.id]) + groups.sort(key=lambda g: set(self.get_closure(cr, uid, [g.id], context))) # classify groups depending on their names classified = {} From a2be1bdbd7541a24cc1f241223fe78b168bd55be Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 2 Aug 2011 15:08:34 +0200 Subject: [PATCH 33/43] add group implications bzr revid: rco@openerp.com-20110802130834-0m7d7tt1ubls7xhn --- openerp/addons/base/security/base_security.xml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openerp/addons/base/security/base_security.xml b/openerp/addons/base/security/base_security.xml index 114c5d2bd8b..7389b45c814 100644 --- a/openerp/addons/base/security/base_security.xml +++ b/openerp/addons/base/security/base_security.xml @@ -5,13 +5,13 @@ - - Administration / Configuration - - Administration / Access Rights + + Administration / Configuration + + Employee @@ -29,12 +29,13 @@ Useability / No One - - Sales / Manager - Sales / User + + Sales / Manager + + From c54b4ea969c48a5b4fa6860178708276e3da1cae Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 2 Aug 2011 16:30:02 +0200 Subject: [PATCH 34/43] redefine user preferences view bzr revid: rco@openerp.com-20110802143002-ukmbqm8g9p0pdnoa --- openerp/addons/base/base_update.xml | 34 +++++++++++++---------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/openerp/addons/base/base_update.xml b/openerp/addons/base/base_update.xml index 1820c8df9f3..55c35b7501a 100644 --- a/openerp/addons/base/base_update.xml +++ b/openerp/addons/base/base_update.xml @@ -77,25 +77,21 @@
- - - - - - - - - - - - - - + + + + + + + + + + + + + + +
From 3683a3aa067b5a6042f9bedbc5532b4f984cec3e Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 3 Aug 2011 08:58:50 +0200 Subject: [PATCH 35/43] move company field under Default Filters in user preferences view bzr revid: rco@openerp.com-20110803065850-gtj6e9jpu8svz7mq --- openerp/addons/base/base_update.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/base_update.xml b/openerp/addons/base/base_update.xml index 55c35b7501a..ef312bd0702 100644 --- a/openerp/addons/base/base_update.xml +++ b/openerp/addons/base/base_update.xml @@ -78,7 +78,6 @@
- @@ -88,6 +87,8 @@ + From c4e7c76d04ffb9abc37eed1908d4fe77df4a114c Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 3 Aug 2011 09:56:59 +0200 Subject: [PATCH 36/43] reshape the users form view bzr revid: rco@openerp.com-20110803075659-hb66ytaifqi4092e --- openerp/addons/base/base_update.xml | 60 ++++++++++++++--------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/openerp/addons/base/base_update.xml b/openerp/addons/base/base_update.xml index ef312bd0702..8877ebf6fe2 100644 --- a/openerp/addons/base/base_update.xml +++ b/openerp/addons/base/base_update.xml @@ -81,18 +81,18 @@ - - - + + + - - - + + @@ -103,45 +103,41 @@ form
- - - - - + + + + + + - + + + + + + + - - + groups="base.group_multi_company"/> - - - - - - - - - - - - - - - - + + + + + + + + From 978f6a9ab619b2fede87fef55d39d17140bef4ac Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 3 Aug 2011 14:08:14 +0200 Subject: [PATCH 37/43] add migration script: copy user emails from their address bzr revid: rco@openerp.com-20110803120814-8entoezaysjgv317 --- openerp/addons/base/__openerp__.py | 2 +- .../base/migrations/1.4/post-user-address.py | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 openerp/addons/base/migrations/1.4/post-user-address.py diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py index c757702d52f..ee0063dc0d9 100644 --- a/openerp/addons/base/__openerp__.py +++ b/openerp/addons/base/__openerp__.py @@ -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.""", diff --git a/openerp/addons/base/migrations/1.4/post-user-address.py b/openerp/addons/base/migrations/1.4/post-user-address.py new file mode 100644 index 00000000000..bb6e2fa487e --- /dev/null +++ b/openerp/addons/base/migrations/1.4/post-user-address.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2011 OpenERP s.a. (). +# +# 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 . +# +############################################################################## + +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""") + From 71b82691b6820d13cf34e130244372ec3784fcbe Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 3 Aug 2011 14:13:17 +0200 Subject: [PATCH 38/43] [IMP] user-address migration: drop column address_id bzr revid: rco@openerp.com-20110803121317-qy3k5q4rjyp89vu9 --- openerp/addons/base/migrations/1.4/post-user-address.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openerp/addons/base/migrations/1.4/post-user-address.py b/openerp/addons/base/migrations/1.4/post-user-address.py index bb6e2fa487e..ca653d7537c 100644 --- a/openerp/addons/base/migrations/1.4/post-user-address.py +++ b/openerp/addons/base/migrations/1.4/post-user-address.py @@ -26,4 +26,6 @@ def migrate(cr, version): SET user_email = res_partner_address.email FROM res_partner_address WHERE res_users.address_id = res_partner_address.id""") + cr.execute(""" + ALTER TABLE res_users DROP COLUMN address_id""") From 03c3e02ba429508e95680b4cfac5c2e1a574647f Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 3 Aug 2011 14:26:18 +0200 Subject: [PATCH 39/43] [IMP] for safety, do not drop column res_users.address_id bzr revid: rco@openerp.com-20110803122618-9821lqc5kvrxti8h --- openerp/addons/base/migrations/1.4/post-user-address.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openerp/addons/base/migrations/1.4/post-user-address.py b/openerp/addons/base/migrations/1.4/post-user-address.py index 60753a4d53e..bb6e2fa487e 100644 --- a/openerp/addons/base/migrations/1.4/post-user-address.py +++ b/openerp/addons/base/migrations/1.4/post-user-address.py @@ -20,14 +20,10 @@ ############################################################################## def migrate(cr, version): - """ copy emails from user.address_id.email to user.user_email, then drop - the column address_id - """ + "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""") - cr.execute(""" - ALTER TABLE res_users DROP COLUMN address_id""") From ca29f41826668521f61b6b8933234347c3da5ece Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 4 Aug 2011 08:59:21 +0200 Subject: [PATCH 40/43] [FIX] data: remove address_id from users bzr revid: rco@openerp.com-20110804065921-vjgj5nh4kkc6d72v --- openerp/addons/base/base_data.xml | 1 - openerp/addons/base/base_demo.xml | 1 - 2 files changed, 2 deletions(-) diff --git a/openerp/addons/base/base_data.xml b/openerp/addons/base/base_data.xml index 75de4a5fee5..00fb83a0796 100644 --- a/openerp/addons/base/base_data.xml +++ b/openerp/addons/base/base_data.xml @@ -1053,7 +1053,6 @@ Administrator - diff --git a/openerp/addons/base/base_demo.xml b/openerp/addons/base/base_demo.xml index 73f6fa400a5..3d1eae3c4bc 100644 --- a/openerp/addons/base/base_demo.xml +++ b/openerp/addons/base/base_demo.xml @@ -19,7 +19,6 @@ demo Demo User Mr Demo - From af70d386afa312ffbe529dd49386aebfd33534cb Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 5 Aug 2011 16:05:14 +0200 Subject: [PATCH 41/43] [IMP] add implied groups to res.groups form bzr revid: rco@openerp.com-20110805140514-0tugvu600nohxldt --- openerp/addons/base/base_update.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openerp/addons/base/base_update.xml b/openerp/addons/base/base_update.xml index d1a2ed84799..dc02dec4781 100644 --- a/openerp/addons/base/base_update.xml +++ b/openerp/addons/base/base_update.xml @@ -24,6 +24,10 @@ + + From 03475f261ddd077d66c7b91e4f9d390b810f0170 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 5 Aug 2011 16:16:51 +0200 Subject: [PATCH 42/43] [IMP] add field view in user preferences view bzr revid: rco@openerp.com-20110805141651-jhb54bawn9v8b2mv --- openerp/addons/base/base_update.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/addons/base/base_update.xml b/openerp/addons/base/base_update.xml index dc02dec4781..d2924828fec 100644 --- a/openerp/addons/base/base_update.xml +++ b/openerp/addons/base/base_update.xml @@ -85,6 +85,7 @@ + From 6ecb84d303319a2eca7273b1f75a34fb98ff72ee Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Fri, 5 Aug 2011 22:50:07 +0200 Subject: [PATCH 43/43] [FIX] for view field, thinking about QuerySet... bzr revid: al@openerp.com-20110805205007-12lvaks9y4y6827p --- openerp/addons/base/res/res_user.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index beac24d44be..6582226c2d5 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -593,6 +593,8 @@ class users_implied(osv.osv): 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