2009-10-20 10:52:23 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2010-01-13 06:25:06 +00:00
#
2008-11-19 09:37:50 +00:00
# OpenERP, Open Source Management Solution
2009-10-14 12:32:15 +00:00
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
2014-03-12 18:06:14 +00:00
# Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>).
2008-06-16 11:00:21 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 12:32:15 +00:00
# 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.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# 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
2009-10-14 12:32:15 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-10-14 12:32:15 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-01-13 06:25:06 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2012-08-14 15:02:12 +00:00
from functools import partial
import logging
2012-03-23 11:05:21 +00:00
from lxml import etree
from lxml . builder import E
2012-12-10 15:27:23 +00:00
2011-10-13 10:47:50 +00:00
import openerp
2012-12-10 15:27:23 +00:00
from openerp import SUPERUSER_ID
2013-03-27 11:10:14 +00:00
from openerp import tools
2011-09-26 12:53:58 +00:00
import openerp . exceptions
2014-04-16 11:22:39 +00:00
from openerp . osv import fields , osv , expression
2012-12-10 15:27:23 +00:00
from openerp . osv . orm import browse_record
from openerp . tools . translate import _
2012-02-21 09:12:57 +00:00
2012-01-24 13:17:05 +00:00
_logger = logging . getLogger ( __name__ )
2013-10-06 14:40:20 +00:00
#----------------------------------------------------------
# Basic res.groups and res.users
#----------------------------------------------------------
class res_groups ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = " res.groups "
2010-11-16 12:53:38 +00:00
_description = " Access Groups "
2011-12-02 16:30:22 +00:00
_rec_name = ' full_name '
2013-05-22 16:32:01 +00:00
_order = ' name '
2011-12-02 16:30:22 +00:00
def _get_full_name ( self , cr , uid , ids , field , arg , context = None ) :
res = { }
for g in self . browse ( cr , uid , ids , context ) :
if g . category_id :
res [ g . id ] = ' %s / %s ' % ( g . category_id . name , g . name )
else :
res [ g . id ] = g . name
return res
2012-09-03 13:25:42 +00:00
def _search_group ( self , cr , uid , obj , name , args , context = None ) :
2012-09-06 06:09:24 +00:00
operand = args [ 0 ] [ 2 ]
operator = args [ 0 ] [ 1 ]
2014-04-16 11:22:39 +00:00
lst = True
if isinstance ( operand , bool ) :
domains = [ [ ( ' name ' , operator , operand ) ] , [ ( ' category_id.name ' , operator , operand ) ] ]
if operator in expression . NEGATIVE_TERM_OPERATORS == ( not operand ) :
return expression . AND ( domains )
else :
return expression . OR ( domains )
if isinstance ( operand , basestring ) :
lst = False
operand = [ operand ]
where = [ ]
for group in operand :
values = filter ( bool , group . split ( ' / ' ) )
group_name = values . pop ( ) . strip ( )
category_name = values and ' / ' . join ( values ) . strip ( ) or group_name
group_domain = [ ( ' name ' , operator , lst and [ group_name ] or group_name ) ]
category_domain = [ ( ' category_id.name ' , operator , lst and [ category_name ] or category_name ) ]
if operator in expression . NEGATIVE_TERM_OPERATORS and not values :
category_domain = expression . OR ( [ category_domain , [ ( ' category_id ' , ' = ' , False ) ] ] )
if ( operator in expression . NEGATIVE_TERM_OPERATORS ) == ( not values ) :
sub_where = expression . AND ( [ group_domain , category_domain ] )
else :
sub_where = expression . OR ( [ group_domain , category_domain ] )
if operator in expression . NEGATIVE_TERM_OPERATORS :
where = expression . AND ( [ where , sub_where ] )
else :
where = expression . OR ( [ where , sub_where ] )
2012-09-06 06:09:24 +00:00
return where
2012-09-18 11:57:17 +00:00
2008-07-22 14:24:36 +00:00
_columns = {
2014-05-21 09:52:05 +00:00
' name ' : fields . char ( ' Name ' , required = True , translate = True ) ,
2011-07-26 10:31:00 +00:00
' users ' : fields . many2many ( ' res.users ' , ' res_groups_users_rel ' , ' gid ' , ' uid ' , ' Users ' ) ,
2008-07-22 14:24:36 +00:00
' model_access ' : fields . one2many ( ' ir.model.access ' , ' group_id ' , ' Access Controls ' ) ,
2010-04-29 12:01:58 +00:00
' rule_groups ' : fields . many2many ( ' ir.rule ' , ' rule_group_rel ' ,
2010-10-21 12:59:05 +00:00
' group_id ' , ' rule_group_id ' , ' Rules ' , domain = [ ( ' global ' , ' = ' , False ) ] ) ,
2008-07-22 14:24:36 +00:00
' menu_access ' : fields . many2many ( ' ir.ui.menu ' , ' ir_ui_menu_group_rel ' , ' gid ' , ' menu_id ' , ' Access Menu ' ) ,
2012-12-18 10:19:16 +00:00
' view_access ' : fields . many2many ( ' ir.ui.view ' , ' ir_ui_view_group_rel ' , ' group_id ' , ' view_id ' , ' Views ' ) ,
2011-12-09 08:02:39 +00:00
' comment ' : fields . text ( ' Comment ' , size = 250 , translate = True ) ,
2011-12-02 16:30:22 +00:00
' category_id ' : fields . many2one ( ' ir.module.category ' , ' Application ' , select = True ) ,
2012-09-03 13:25:42 +00:00
' full_name ' : fields . function ( _get_full_name , type = ' char ' , string = ' Group Name ' , fnct_search = _search_group ) ,
2008-07-22 14:24:36 +00:00
}
2011-12-02 16:30:22 +00:00
2008-07-22 14:24:36 +00:00
_sql_constraints = [
2014-04-29 07:11:25 +00:00
( ' name_uniq ' , ' unique (category_id, name) ' , ' The name of the group must be unique within an application! ' )
2008-07-22 14:24:36 +00:00
]
2010-02-16 06:31:14 +00:00
2011-12-09 08:03:19 +00:00
def search ( self , cr , uid , args , offset = 0 , limit = None , order = None , context = None , count = False ) :
# add explicit ordering if search is sorted on full_name
if order and order . startswith ( ' full_name ' ) :
2013-10-06 15:03:09 +00:00
ids = super ( res_groups , self ) . search ( cr , uid , args , context = context )
2011-12-09 08:03:19 +00:00
gs = self . browse ( cr , uid , ids , context )
gs . sort ( key = lambda g : g . full_name , reverse = order . endswith ( ' DESC ' ) )
gs = gs [ offset : offset + limit ] if limit else gs [ offset : ]
return map ( int , gs )
2013-10-06 15:03:09 +00:00
return super ( res_groups , self ) . search ( cr , uid , args , offset , limit , order , context , count )
2011-12-09 08:03:19 +00:00
2010-11-23 15:20:29 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
2009-09-17 07:27:12 +00:00
group_name = self . read ( cr , uid , [ id ] , [ ' name ' ] ) [ 0 ] [ ' name ' ]
2010-10-04 14:08:23 +00:00
default . update ( { ' name ' : _ ( ' %s (copy) ' ) % group_name } )
2013-10-06 15:03:09 +00:00
return super ( res_groups , self ) . copy ( cr , uid , id , default , context )
2010-04-29 12:01:58 +00:00
2008-07-22 14:24:36 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
if ' name ' in vals :
if vals [ ' name ' ] . startswith ( ' - ' ) :
raise osv . except_osv ( _ ( ' Error ' ) ,
_ ( ' The name of the group can not start with " - " ' ) )
2013-10-06 15:03:09 +00:00
res = super ( res_groups , self ) . write ( cr , uid , ids , vals , context = context )
2013-03-27 11:10:14 +00:00
self . pool [ ' ir.model.access ' ] . call_cache_clearing_methods ( cr )
2008-07-22 14:24:36 +00:00
return res
2007-08-09 06:06:27 +00:00
2012-08-14 15:02:12 +00:00
class res_users ( osv . osv ) :
2012-08-10 08:16:32 +00:00
""" User class. A res.users record models an OpenERP user and is different
from an employee .
res . users class now inherits from res . partner . The partner model is
used to store the data related to the partner : lang , name , address ,
avatar , . . . The user model is now dedicated to technical data .
"""
2009-02-04 14:36:29 +00:00
__admin_ids = { }
2010-01-29 06:06:44 +00:00
_uid_cache = { }
2012-08-10 08:16:32 +00:00
_inherits = {
' res.partner ' : ' partner_id ' ,
}
2008-07-22 14:24:36 +00:00
_name = " res.users "
2012-06-15 13:41:22 +00:00
_description = ' Users '
2010-01-13 06:25:06 +00:00
2011-01-06 17:38:28 +00:00
def _set_new_password ( self , cr , uid , id , name , value , args , context = None ) :
2011-01-07 12:12:01 +00:00
if value is False :
# Do not update the password if no value is provided, ignore silently.
# For example web client submits False values for all empty fields.
return
2011-01-06 17:38:28 +00:00
if uid == id :
# To change their own password users must use the client-specific change password wizard,
# so that the new password is immediately used for further RPC requests, otherwise the user
# will face unexpected 'Access Denied' exceptions.
raise osv . except_osv ( _ ( ' Operation Canceled ' ) , _ ( ' Please use the change password wizard (in User Preferences or User menu) to change your own password. ' ) )
self . write ( cr , uid , id , { ' password ' : value } )
2011-05-17 07:14:02 +00:00
def _get_password ( self , cr , uid , ids , arg , karg , context = None ) :
return dict . fromkeys ( ids , ' ' )
2012-09-18 11:57:17 +00:00
2008-07-22 14:24:36 +00:00
_columns = {
2011-11-14 21:26:27 +00:00
' id ' : fields . integer ( ' ID ' ) ,
2012-08-10 08:16:32 +00:00
' login_date ' : fields . date ( ' Latest connection ' , select = 1 ) ,
' partner_id ' : fields . many2one ( ' res.partner ' , required = True ,
2012-12-17 21:46:45 +00:00
string = ' Related Partner ' , ondelete = ' restrict ' ,
2012-08-10 08:16:32 +00:00
help = ' Partner-related data of the user ' ) ,
2010-01-24 15:57:12 +00:00
' login ' : fields . char ( ' Login ' , size = 64 , required = True ,
2012-08-10 08:16:32 +00:00
help = " Used to log into the system " ) ,
' password ' : fields . char ( ' Password ' , size = 64 , invisible = True ,
help = " Keep empty if you don ' t want the user to be able to connect on the system. " ) ,
2012-01-04 13:30:27 +00:00
' new_password ' : fields . function ( _get_password , type = ' char ' , size = 64 ,
2012-08-10 08:16:32 +00:00
fnct_inv = _set_new_password , string = ' Set Password ' ,
help = " Specify a value only when creating a user or if you ' re " \
" changing the user ' s password, otherwise leave empty. After " \
" a change of password, the user has to login again. " ) ,
2014-06-27 09:31:24 +00:00
' signature ' : fields . html ( ' Signature ' ) ,
2008-07-22 14:24:36 +00:00
' active ' : fields . boolean ( ' Active ' ) ,
2014-04-29 12:13:49 +00:00
' action_id ' : fields . many2one ( ' ir.actions.actions ' , ' Home Action ' , help = " If specified, this action will be opened at log on for this user, in addition to the standard menu. " ) ,
2008-07-22 14:24:36 +00:00
' groups_id ' : fields . many2many ( ' res.groups ' , ' res_groups_users_rel ' , ' uid ' , ' gid ' , ' Groups ' ) ,
2010-06-04 00:39:40 +00:00
# Special behavior for this field: res.company.search() will only return the companies
# available to the current user (should be the user's companies?), when the user_preference
# context is set.
2010-01-24 15:57:12 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True ,
2012-08-10 08:16:32 +00:00
help = ' The company this user is currently working for. ' , context = { ' user_preference ' : True } ) ,
2010-02-28 08:02:14 +00:00
' company_ids ' : fields . many2many ( ' res.company ' , ' res_company_users_rel ' , ' user_id ' , ' cid ' , ' Companies ' ) ,
2008-07-22 14:24:36 +00:00
}
2010-05-14 08:34:02 +00:00
2013-03-27 13:21:42 +00:00
def on_change_login ( self , cr , uid , ids , login , context = None ) :
2014-03-04 11:14:52 +00:00
if login and tools . single_email_re . match ( login ) :
return { ' value ' : { ' email ' : login } }
return { }
2013-03-27 13:21:42 +00:00
2013-03-04 10:58:17 +00:00
def onchange_state ( self , cr , uid , ids , state_id , context = None ) :
partner_ids = [ user . partner_id . id for user in self . browse ( cr , uid , ids , context = context ) ]
return self . pool . get ( ' res.partner ' ) . onchange_state ( cr , uid , partner_ids , state_id , context = context )
2012-08-13 09:16:27 +00:00
def onchange_type ( self , cr , uid , ids , is_company , context = None ) :
""" Wrapper on the user.partner onchange_type, because some calls to the
partner form view applied to the user may trigger the
partner . onchange_type method , but applied to the user object .
"""
partner_ids = [ user . partner_id . id for user in self . browse ( cr , uid , ids , context = context ) ]
2013-03-27 11:10:14 +00:00
return self . pool [ ' res.partner ' ] . onchange_type ( cr , uid , partner_ids , is_company , context = context )
2012-08-13 09:16:27 +00:00
2012-08-13 10:18:47 +00:00
def onchange_address ( self , cr , uid , ids , use_parent_address , parent_id , context = None ) :
""" Wrapper on the user.partner onchange_address, because some calls to the
partner form view applied to the user may trigger the
partner . onchange_type method , but applied to the user object .
"""
partner_ids = [ user . partner_id . id for user in self . browse ( cr , uid , ids , context = context ) ]
2013-03-27 11:10:14 +00:00
return self . pool [ ' res.partner ' ] . onchange_address ( cr , uid , partner_ids , use_parent_address , parent_id , context = context )
2012-08-13 10:18:47 +00:00
2010-05-07 14:20:25 +00:00
def _check_company ( self , cr , uid , ids , context = None ) :
2010-05-10 18:32:49 +00:00
return all ( ( ( this . company_id in this . company_ids ) or not this . company_ids ) for this in self . browse ( cr , uid , ids , context ) )
2010-05-07 14:20:25 +00:00
_constraints = [
2010-06-04 00:39:40 +00:00
( _check_company , ' The chosen company is not in the allowed companies for this user ' , [ ' company_id ' , ' company_ids ' ] ) ,
2010-05-07 14:20:25 +00:00
]
2008-07-22 14:24:36 +00:00
_sql_constraints = [
2010-10-28 12:11:43 +00:00
( ' login_key ' , ' UNIQUE (login) ' , ' You can not have two users with the same login ! ' )
2008-07-22 14:24:36 +00:00
]
2009-02-04 14:36:29 +00:00
2010-05-07 14:20:25 +00:00
def _get_company ( self , cr , uid , context = None , uid2 = False ) :
2010-02-02 13:51:00 +00:00
if not uid2 :
uid2 = uid
2013-03-27 11:10:14 +00:00
user = self . pool [ ' res.users ' ] . read ( cr , uid , uid2 , [ ' company_id ' ] , context )
2010-02-19 11:05:41 +00:00
company_id = user . get ( ' company_id ' , False )
return company_id and company_id [ 0 ] or False
2008-06-30 15:33:15 +00:00
2010-05-07 14:20:25 +00:00
def _get_companies ( self , cr , uid , context = None ) :
c = self . _get_company ( cr , uid , context )
if c :
return [ c ]
return False
2010-10-01 16:37:17 +00:00
def _get_group ( self , cr , uid , context = None ) :
2010-10-04 18:46:30 +00:00
dataobj = self . pool . get ( ' ir.model.data ' )
2010-10-18 07:37:15 +00:00
result = [ ]
try :
2012-08-31 13:53:09 +00:00
dummy , group_id = dataobj . get_object_reference ( cr , SUPERUSER_ID , ' base ' , ' group_user ' )
2010-10-18 07:37:15 +00:00
result . append ( group_id )
2012-08-31 13:53:09 +00:00
dummy , group_id = dataobj . get_object_reference ( cr , SUPERUSER_ID , ' base ' , ' group_partner_manager ' )
2010-10-18 07:37:15 +00:00
result . append ( group_id )
except ValueError :
# If these groups does not exists anymore
pass
return result
2008-11-21 11:30:22 +00:00
2008-07-22 14:24:36 +00:00
_defaults = {
2012-09-07 09:22:06 +00:00
' password ' : ' ' ,
' active ' : True ,
2012-08-10 15:13:07 +00:00
' customer ' : False ,
2008-07-22 14:24:36 +00:00
' company_id ' : _get_company ,
2010-05-07 14:20:25 +00:00
' company_ids ' : _get_companies ,
2008-11-21 11:30:22 +00:00
' groups_id ' : _get_group ,
2013-03-27 11:10:14 +00:00
' image ' : lambda self , cr , uid , ctx = { } : self . pool [ ' res.partner ' ] . _get_default_image ( cr , uid , False , ctx , colorize = True ) ,
2008-07-22 14:24:36 +00:00
}
2010-06-10 14:57:22 +00:00
2012-09-18 11:57:17 +00:00
# User can write on a few of his own fields (but not his groups for example)
2014-04-29 12:13:49 +00:00
SELF_WRITEABLE_FIELDS = [ ' password ' , ' signature ' , ' action_id ' , ' company_id ' , ' email ' , ' name ' , ' image ' , ' image_medium ' , ' image_small ' , ' lang ' , ' tz ' ]
2012-09-18 11:57:17 +00:00
# User can read a few of his own fields
2012-12-12 21:01:37 +00:00
SELF_READABLE_FIELDS = [ ' signature ' , ' company_id ' , ' login ' , ' email ' , ' name ' , ' image ' , ' image_medium ' , ' image_small ' , ' lang ' , ' tz ' , ' tz_offset ' , ' groups_id ' , ' partner_id ' , ' __last_update ' ]
2012-09-17 16:02:41 +00:00
def read ( self , cr , uid , ids , fields = None , context = None , load = ' _classic_read ' ) :
def override_password ( o ) :
if ' password ' in o and ( ' id ' not in o or o [ ' id ' ] != uid ) :
o [ ' password ' ] = ' ******** '
return o
2012-09-28 16:39:56 +00:00
if fields and ( ids == [ uid ] or ids == uid ) :
2012-09-17 16:02:41 +00:00
for key in fields :
2012-09-28 16:39:56 +00:00
if not ( key in self . SELF_READABLE_FIELDS or key . startswith ( ' context_ ' ) ) :
2012-09-17 16:02:41 +00:00
break
else :
# safe fields only, so we read as super-user to bypass access rights
2012-09-18 12:02:30 +00:00
uid = SUPERUSER_ID
2012-09-17 16:02:41 +00:00
result = super ( res_users , self ) . read ( cr , uid , ids , fields = fields , context = context , load = load )
2013-03-27 11:10:14 +00:00
canwrite = self . pool [ ' ir.model.access ' ] . check ( cr , uid , ' res.users ' , ' write ' , False )
2012-09-17 16:02:41 +00:00
if not canwrite :
if isinstance ( ids , ( int , long ) ) :
result = override_password ( result )
else :
result = map ( override_password , result )
return result
2010-07-12 14:22:05 +00:00
2013-04-11 09:43:36 +00:00
def create ( self , cr , uid , vals , context = None ) :
user_id = super ( res_users , self ) . create ( cr , uid , vals , context = context )
2013-05-13 06:07:34 +00:00
user = self . browse ( cr , uid , user_id , context = context )
if user . partner_id . company_id :
user . partner_id . write ( { ' company_id ' : user . company_id . id } )
2013-04-11 09:43:36 +00:00
return user_id
2010-06-04 11:40:55 +00:00
def write ( self , cr , uid , ids , values , context = None ) :
2010-06-10 14:57:22 +00:00
if not hasattr ( ids , ' __iter__ ' ) :
ids = [ ids ]
2010-06-04 11:40:55 +00:00
if ids == [ uid ] :
for key in values . keys ( ) :
2010-07-12 14:22:05 +00:00
if not ( key in self . SELF_WRITEABLE_FIELDS or key . startswith ( ' context_ ' ) ) :
2010-06-04 11:40:55 +00:00
break
else :
2010-10-12 07:08:57 +00:00
if ' company_id ' in values :
2012-08-31 13:53:09 +00:00
if not ( values [ ' company_id ' ] in self . read ( cr , SUPERUSER_ID , uid , [ ' company_ids ' ] , context = context ) [ ' company_ids ' ] ) :
2010-10-12 07:08:57 +00:00
del values [ ' company_id ' ]
uid = 1 # safe fields only, so we write as super-user to bypass access rights
2010-07-12 14:22:05 +00:00
2012-08-14 15:54:14 +00:00
res = super ( res_users , self ) . write ( cr , uid , ids , values , context = context )
2013-04-11 09:43:36 +00:00
if ' company_id ' in values :
2013-05-13 06:07:34 +00:00
for user in self . browse ( cr , uid , ids , context = context ) :
# if partner is global we keep it that way
2013-08-01 06:27:06 +00:00
if user . partner_id . company_id and user . partner_id . company_id . id != values [ ' company_id ' ] :
2013-05-13 06:07:34 +00:00
user . partner_id . write ( { ' company_id ' : user . company_id . id } )
2010-06-10 14:57:22 +00:00
# clear caches linked to the users
2013-03-27 11:10:14 +00:00
self . pool [ ' ir.model.access ' ] . call_cache_clearing_methods ( cr )
clear = partial ( self . pool [ ' ir.rule ' ] . clear_cache , cr )
2010-06-10 14:57:22 +00:00
map ( clear , ids )
2010-12-14 16:29:05 +00:00
db = cr . dbname
if db in self . _uid_cache :
for id in ids :
if id in self . _uid_cache [ db ] :
del self . _uid_cache [ db ] [ id ]
2012-12-19 10:13:15 +00:00
self . context_get . clear_cache ( self )
2008-07-22 14:24:36 +00:00
return res
2006-12-07 13:41:40 +00:00
2009-01-21 11:19:56 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2008-07-22 14:24:36 +00:00
if 1 in ids :
2008-09-02 21:31:44 +00:00
raise osv . except_osv ( _ ( ' Can not remove root user! ' ) , _ ( ' You can not remove the admin user as it is used internally for resources created by OpenERP (updates, module installation, ...) ' ) )
2010-12-14 16:29:05 +00:00
db = cr . dbname
if db in self . _uid_cache :
for id in ids :
if id in self . _uid_cache [ db ] :
del self . _uid_cache [ db ] [ id ]
2012-08-14 15:54:14 +00:00
return super ( res_users , self ) . unlink ( cr , uid , ids , context = context )
2008-06-20 11:49:23 +00:00
2009-12-09 11:42:41 +00:00
def name_search ( self , cr , user , name = ' ' , args = None , operator = ' ilike ' , context = None , limit = 100 ) :
2008-07-22 14:24:36 +00:00
if not args :
args = [ ]
if not context :
context = { }
ids = [ ]
if name :
2012-09-03 08:46:25 +00:00
ids = self . search ( cr , user , [ ( ' login ' , ' = ' , name ) ] + args , limit = limit , context = context )
2008-07-22 14:24:36 +00:00
if not ids :
2012-09-03 08:46:25 +00:00
ids = self . search ( cr , user , [ ( ' name ' , operator , name ) ] + args , limit = limit , context = context )
return self . name_get ( cr , user , ids , context = context )
2008-06-20 11:49:23 +00:00
2010-11-23 15:20:29 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
2010-09-16 11:43:37 +00:00
user2copy = self . read ( cr , uid , [ id ] , [ ' login ' , ' name ' ] ) [ 0 ]
2012-10-05 10:12:20 +00:00
default = dict ( default or { } )
if ( ' name ' not in default ) and ( ' partner_id ' not in default ) :
default [ ' name ' ] = _ ( " %s (copy) " ) % user2copy [ ' name ' ]
if ' login ' not in default :
default [ ' login ' ] = _ ( " %s (copy) " ) % user2copy [ ' login ' ]
return super ( res_users , self ) . copy ( cr , uid , id , default , context )
2008-06-16 18:27:17 +00:00
2012-12-17 21:47:00 +00:00
@tools.ormcache ( skiparg = 2 )
2008-09-22 11:13:18 +00:00
def context_get ( self , cr , uid , context = None ) :
2012-08-31 13:53:09 +00:00
user = self . browse ( cr , SUPERUSER_ID , uid , context )
2008-07-22 14:24:36 +00:00
result = { }
2012-08-10 08:16:32 +00:00
for k in self . _all_columns . keys ( ) :
2008-07-22 14:24:36 +00:00
if k . startswith ( ' context_ ' ) :
2012-08-10 08:16:32 +00:00
context_key = k [ 8 : ]
2012-11-29 21:00:33 +00:00
elif k in [ ' lang ' , ' tz ' ] :
2012-08-10 08:16:32 +00:00
context_key = k
else :
context_key = False
if context_key :
2010-02-11 05:29:24 +00:00
res = getattr ( user , k ) or False
2010-02-09 13:46:52 +00:00
if isinstance ( res , browse_record ) :
2010-02-11 05:29:24 +00:00
res = res . id
2012-08-10 08:16:32 +00:00
result [ context_key ] = res or False
2008-07-22 14:24:36 +00:00
return result
2008-06-16 18:27:17 +00:00
2010-11-23 15:20:29 +00:00
def action_get ( self , cr , uid , context = None ) :
2013-03-27 11:10:14 +00:00
dataobj = self . pool [ ' ir.model.data ' ]
2012-08-31 13:53:09 +00:00
data_id = dataobj . _get_id ( cr , SUPERUSER_ID , ' base ' , ' action_res_users_my ' )
2010-11-23 15:20:29 +00:00
return dataobj . browse ( cr , uid , data_id , context = context ) . res_id
2010-01-13 06:25:06 +00:00
2012-08-15 21:06:45 +00:00
def check_super ( self , passwd ) :
if passwd == tools . config [ ' admin_passwd ' ] :
return True
else :
raise openerp . exceptions . AccessDenied ( )
def check_credentials ( self , cr , uid , password ) :
""" Override this method to plug additional authentication methods """
2012-08-31 13:53:09 +00:00
res = self . search ( cr , SUPERUSER_ID , [ ( ' id ' , ' = ' , uid ) , ( ' password ' , ' = ' , password ) ] )
2012-08-15 21:06:45 +00:00
if not res :
raise openerp . exceptions . AccessDenied ( )
def login ( self , db , login , password ) :
if not password :
return False
user_id = False
2014-04-09 09:56:04 +00:00
cr = self . pool . cursor ( )
2012-08-15 21:06:45 +00:00
try :
# autocommit: our single update request will be performed atomically.
# (In this way, there is no opportunity to have two transactions
# interleaving their cr.execute()..cr.commit() calls and have one
# of them rolled back due to a concurrent access.)
cr . autocommit ( True )
# check if user exists
2012-08-31 13:53:09 +00:00
res = self . search ( cr , SUPERUSER_ID , [ ( ' login ' , ' = ' , login ) ] )
2012-08-15 21:06:45 +00:00
if res :
user_id = res [ 0 ]
# check credentials
self . check_credentials ( cr , user_id , password )
# We effectively unconditionally write the res_users line.
# Even w/ autocommit there's a chance the user row will be locked,
# in which case we can't delay the login just for the purpose of
# update the last login date - hence we use FOR UPDATE NOWAIT to
# try to get the lock - fail-fast
# Failing to acquire the lock on the res_users row probably means
# another request is holding it. No big deal, we don't want to
# prevent/delay login in that case. It will also have been logged
# as a SQL error, if anyone cares.
try :
2012-11-22 22:12:00 +00:00
cr . execute ( " SELECT id FROM res_users WHERE id= %s FOR UPDATE NOWAIT " , ( user_id , ) , log_exceptions = False )
2012-10-15 10:01:48 +00:00
cr . execute ( " UPDATE res_users SET login_date = now() AT TIME ZONE ' UTC ' WHERE id= %s " , ( user_id , ) )
2012-11-20 13:16:10 +00:00
except Exception :
_logger . debug ( " Failed to update last_login for db: %s login: %s " , db , login , exc_info = True )
2012-08-15 21:06:45 +00:00
except openerp . exceptions . AccessDenied :
_logger . info ( " Login failed for db: %s login: %s " , db , login )
user_id = False
finally :
cr . close ( )
return user_id
2011-10-13 10:47:50 +00:00
def authenticate ( self , db , login , password , user_agent_env ) :
""" Verifies and returns the user ID corresponding to the given
` ` login ` ` and ` ` password ` ` combination , or False if there was
no matching user .
: param str db : the database on which user is trying to authenticate
: param str login : username
: param str password : user password
: param dict user_agent_env : environment dictionary describing any
relevant environment attributes
"""
uid = self . login ( db , login , password )
if uid == openerp . SUPERUSER_ID :
# Successfully logged in as admin!
# Attempt to guess the web base url...
2011-10-13 14:32:45 +00:00
if user_agent_env and user_agent_env . get ( ' base_location ' ) :
2014-04-09 09:56:04 +00:00
cr = self . pool . cursor ( )
2011-10-13 10:47:50 +00:00
try :
2012-08-15 21:06:45 +00:00
base = user_agent_env [ ' base_location ' ]
2013-04-04 13:07:04 +00:00
ICP = self . pool [ ' ir.config_parameter ' ]
2013-03-01 09:55:51 +00:00
if not ICP . get_param ( cr , uid , ' web.base.url.freeze ' ) :
ICP . set_param ( cr , uid , ' web.base.url ' , base )
2011-10-13 10:47:50 +00:00
cr . commit ( )
except Exception :
2012-01-24 13:17:05 +00:00
_logger . exception ( " Failed to update web.base.url configuration parameter " )
2011-10-13 10:47:50 +00:00
finally :
cr . close ( )
return uid
2010-01-29 06:06:44 +00:00
def check ( self , db , uid , passwd ) :
2012-08-15 21:06:45 +00:00
""" Verifies that the given (uid, password) is authorized for the database ``db`` and
2011-04-28 15:39:01 +00:00
raise an exception if it is not . """
2010-01-29 06:06:44 +00:00
if not passwd :
2011-04-28 15:39:01 +00:00
# empty passwords disallowed for obvious security reasons
2011-09-26 12:53:58 +00:00
raise openerp . exceptions . AccessDenied ( )
2011-01-10 13:52:26 +00:00
if self . _uid_cache . get ( db , { } ) . get ( uid ) == passwd :
2011-04-28 15:39:01 +00:00
return
2014-04-09 09:56:04 +00:00
cr = self . pool . cursor ( )
2010-11-23 15:20:29 +00:00
try :
2012-08-15 21:06:45 +00:00
self . check_credentials ( cr , uid , passwd )
2011-04-28 15:39:01 +00:00
if self . _uid_cache . has_key ( db ) :
2012-08-15 21:06:45 +00:00
self . _uid_cache [ db ] [ uid ] = passwd
2011-04-28 15:39:01 +00:00
else :
self . _uid_cache [ db ] = { uid : passwd }
2010-11-23 15:20:29 +00:00
finally :
cr . close ( )
2010-01-29 06:06:44 +00:00
2011-01-07 09:39:29 +00:00
def change_password ( self , cr , uid , old_passwd , new_passwd , context = None ) :
2011-01-06 17:38:28 +00:00
""" Change current user password. Old password must be provided explicitly
to prevent hijacking an existing user session , or for cases where the cleartext
password is not used to authenticate requests .
: return : True
2011-09-26 12:53:58 +00:00
: raise : openerp . exceptions . AccessDenied when old password is wrong
2011-01-07 12:12:01 +00:00
: raise : except_osv when new password is not set or empty
2011-01-06 17:38:28 +00:00
"""
2011-01-07 12:12:01 +00:00
self . check ( cr . dbname , uid , old_passwd )
if new_passwd :
2011-01-07 09:39:29 +00:00
return self . write ( cr , uid , uid , { ' password ' : new_passwd } )
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( " Setting empty passwords is not allowed for security reasons! " ) )
2011-01-03 13:23:26 +00:00
2012-08-08 19:12:44 +00:00
def preference_save ( self , cr , uid , ids , context = None ) :
return {
' type ' : ' ir.actions.client ' ,
' tag ' : ' reload ' ,
}
2012-10-24 11:17:46 +00:00
def preference_change_password ( self , cr , uid , ids , context = None ) :
return {
' type ' : ' ir.actions.client ' ,
' tag ' : ' change_password ' ,
' target ' : ' new ' ,
}
2012-05-18 14:36:25 +00:00
def has_group ( self , cr , uid , group_ext_id ) :
""" Checks whether user belongs to given group.
: param str group_ext_id : external ID ( XML ID ) of the group .
Must be provided in fully - qualified form ( ` ` module . ext_id ` ` ) , as there
is no implicit module to use . .
: return : True if the current user is a member of the group with the
given external ID ( XML ID ) , else False .
"""
assert group_ext_id and ' . ' in group_ext_id , " External ID must be fully qualified "
module , ext_id = group_ext_id . split ( ' . ' )
2012-09-18 11:57:17 +00:00
cr . execute ( """ SELECT 1 FROM res_groups_users_rel WHERE uid= %s AND gid IN
2012-05-18 14:36:25 +00:00
( SELECT res_id FROM ir_model_data WHERE module = % s AND name = % s ) """ ,
( uid , module , ext_id ) )
return bool ( cr . fetchone ( ) )
2013-10-06 14:40:20 +00:00
#----------------------------------------------------------
# Implied groups
2011-07-29 14:04:00 +00:00
#
2013-10-06 14:40:20 +00:00
# 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).
#----------------------------------------------------------
2011-07-29 14:04:00 +00:00
2011-12-05 11:16:09 +00:00
class cset ( object ) :
""" A cset (constrained set) is a set of elements that may be constrained to
be a subset of other csets . Elements added to a cset are automatically
added to its supersets . Cycles in the subset constraints are supported .
"""
def __init__ ( self , xs ) :
self . supersets = set ( )
self . elements = set ( xs )
def subsetof ( self , other ) :
if other is not self :
self . supersets . add ( other )
other . update ( self . elements )
def update ( self , xs ) :
xs = set ( xs ) - self . elements
if xs : # xs will eventually be empty in case of a cycle
self . elements . update ( xs )
for s in self . supersets :
s . update ( xs )
def __iter__ ( self ) :
return iter ( self . elements )
2011-12-07 08:43:32 +00:00
def concat ( ls ) :
""" return the concatenation of a list of iterables """
res = [ ]
for l in ls : res . extend ( l )
return res
2011-12-05 11:16:09 +00:00
2011-07-29 14:04:00 +00:00
class groups_implied ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_inherit = ' res.groups '
2011-12-02 14:04:17 +00:00
def _get_trans_implied ( self , cr , uid , ids , field , arg , context = None ) :
" computes the transitive closure of relation implied_ids "
2011-12-05 11:16:09 +00:00
memo = { } # use a memo for performance and cycle avoidance
def computed_set ( g ) :
if g not in memo :
memo [ g ] = cset ( g . implied_ids )
for h in g . implied_ids :
computed_set ( h ) . subsetof ( memo [ g ] )
return memo [ g ]
2011-12-02 14:04:17 +00:00
res = { }
2012-08-31 13:53:09 +00:00
for g in self . browse ( cr , SUPERUSER_ID , ids , context ) :
2011-12-05 11:16:09 +00:00
res [ g . id ] = map ( int , computed_set ( g ) )
2011-12-02 14:04:17 +00:00
return res
2008-07-22 14:24:36 +00:00
_columns = {
2011-07-27 13:11:47 +00:00
' implied_ids ' : fields . many2many ( ' res.groups ' , ' res_groups_implied_rel ' , ' gid ' , ' hid ' ,
string = ' Inherits ' , help = ' Users of this group automatically inherit those groups ' ) ,
2011-12-02 14:04:17 +00:00
' trans_implied_ids ' : fields . function ( _get_trans_implied ,
type = ' many2many ' , relation = ' res.groups ' , string = ' Transitively inherits ' ) ,
2008-07-22 14:24:36 +00:00
}
2010-05-11 08:30:33 +00:00
2011-07-29 12:42:35 +00:00
def create ( self , cr , uid , values , context = None ) :
2011-08-01 14:51:43 +00:00
users = values . pop ( ' users ' , None )
2011-07-29 14:04:00 +00:00
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
2010-11-24 16:41:05 +00:00
2011-07-29 12:42:35 +00:00
def write ( self , cr , uid , ids , values , context = None ) :
2011-07-29 14:04:00 +00:00
res = super ( groups_implied , self ) . write ( cr , uid , ids , values , context )
2011-07-29 12:42:35 +00:00
if values . get ( ' users ' ) or values . get ( ' implied_ids ' ) :
2011-12-02 14:04:17 +00:00
# add all implied groups (to all users of each group)
2014-03-26 14:05:09 +00:00
for g in self . browse ( cr , uid , ids , context = context ) :
2011-12-02 14:04:17 +00:00
gids = map ( int , g . trans_implied_ids )
vals = { ' users ' : [ ( 4 , u . id ) for u in g . users ] }
super ( groups_implied , self ) . write ( cr , uid , gids , vals , context )
2011-07-29 12:42:35 +00:00
return res
2010-05-11 08:30:33 +00:00
2011-07-29 14:04:00 +00:00
class users_implied ( osv . osv ) :
_inherit = ' res.users '
def create ( self , cr , uid , values , context = None ) :
2011-09-22 09:38:19 +00:00
groups = values . pop ( ' groups_id ' , None )
2011-07-29 14:04:00 +00:00
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 )
2014-01-17 14:38:53 +00:00
self . pool [ ' ir.ui.view ' ] . clear_cache ( )
2011-07-29 14:04:00 +00:00
return user_id
2007-07-19 13:47:25 +00:00
2011-07-29 14:04:00 +00:00
def write ( self , cr , uid , ids , values , context = None ) :
2011-08-05 20:50:07 +00:00
if not isinstance ( ids , list ) :
ids = [ ids ]
2011-07-29 14:04:00 +00:00
res = super ( users_implied , self ) . write ( cr , uid , ids , values , context )
if values . get ( ' groups_id ' ) :
# add implied groups for all users
2011-12-06 16:23:56 +00:00
for user in self . browse ( cr , uid , ids ) :
2011-12-07 08:43:32 +00:00
gs = set ( concat ( [ g . trans_implied_ids for g in user . groups_id ] ) )
vals = { ' groups_id ' : [ ( 4 , g . id ) for g in gs ] }
super ( users_implied , self ) . write ( cr , uid , [ user . id ] , vals , context )
2014-01-17 14:38:53 +00:00
self . pool [ ' ir.ui.view ' ] . clear_cache ( )
2011-07-29 14:04:00 +00:00
return res
2013-10-06 14:40:20 +00:00
#----------------------------------------------------------
# Vitrual checkbox and selection for res.user form view
2011-07-29 14:04:00 +00:00
#
# 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:
2011-12-06 09:10:11 +00:00
# - Groups are shown by application, with boolean and/or selection fields.
# Selection fields typically defines a role "Name" for the given application.
# - Uncategorized groups are presented as boolean fields and grouped in a
2011-07-29 14:04:00 +00:00
# section "Others".
#
2011-12-07 08:50:02 +00:00
# The user form view is modified by an inherited view (base.user_groups_view);
# the inherited view replaces the field 'groups_id' by a set of reified group
# fields (boolean or selection fields). The arch of that view is regenerated
# each time groups are changed.
#
2011-07-28 14:51:31 +00:00
# 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}
2013-10-06 14:40:20 +00:00
#----------------------------------------------------------
2011-07-28 14:51:31 +00:00
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_ ' )
2011-12-02 14:14:10 +00:00
def is_reified_group ( name ) :
2011-07-29 09:46:43 +00:00
return is_boolean_group ( name ) or is_boolean_groups ( name ) or is_selection_groups ( name )
2011-07-28 14:51:31 +00:00
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 ( ' _ ' ) )
2011-07-29 10:16:20 +00:00
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 :
2011-12-06 09:10:11 +00:00
( yes if f ( x ) else nos ) . append ( x )
2011-07-29 10:16:20 +00:00
return yes , nos
2011-07-29 07:40:25 +00:00
2011-07-28 07:25:44 +00:00
2011-12-06 09:10:11 +00:00
class groups_view ( osv . osv ) :
_inherit = ' res.groups '
def create ( self , cr , uid , values , context = None ) :
res = super ( groups_view , self ) . create ( cr , uid , values , context )
self . update_user_groups_view ( cr , uid , context )
return res
def write ( self , cr , uid , ids , values , context = None ) :
res = super ( groups_view , self ) . write ( cr , uid , ids , values , context )
self . update_user_groups_view ( cr , uid , context )
return res
def unlink ( self , cr , uid , ids , context = None ) :
res = super ( groups_view , self ) . unlink ( cr , uid , ids , context )
self . update_user_groups_view ( cr , uid , context )
return res
def update_user_groups_view ( self , cr , uid , context = None ) :
# the view with id 'base.user_groups_view' inherits the user form view,
# and introduces the reified group fields
2014-01-16 13:40:56 +00:00
# we have to try-catch this, because at first init the view does not exist
# but we are already creating some basic groups
2014-01-29 03:20:53 +00:00
view = self . pool [ ' ir.model.data ' ] . xmlid_to_object ( cr , SUPERUSER_ID , ' base.user_groups_view ' , context = context )
2014-01-16 13:40:56 +00:00
if view and view . exists ( ) and view . _table_name == ' ir.ui.view ' :
2011-12-06 13:51:48 +00:00
xml1 , xml2 = [ ] , [ ]
2012-03-08 17:43:04 +00:00
xml1 . append ( E . separator ( string = _ ( ' Application ' ) , colspan = " 4 " ) )
2011-12-06 13:51:48 +00:00
for app , kind , gs in self . get_groups_by_application ( cr , uid , context ) :
2012-04-02 14:50:11 +00:00
# hide groups in category 'Hidden' (except to group_no_one)
2012-04-03 08:27:43 +00:00
attrs = { ' groups ' : ' base.group_no_one ' } if app and app . xml_id == ' base.module_category_hidden ' else { }
2011-12-06 13:51:48 +00:00
if kind == ' selection ' :
# application name with a selection field
field_name = name_selection_groups ( map ( int , gs ) )
2012-04-03 08:27:43 +00:00
xml1 . append ( E . field ( name = field_name , * * attrs ) )
2012-03-08 17:43:04 +00:00
xml1 . append ( E . newline ( ) )
2011-07-29 15:15:24 +00:00
else :
2011-12-06 13:51:48 +00:00
# application separator with boolean fields
app_name = app and app . name or _ ( ' Other ' )
2012-04-03 08:27:43 +00:00
xml2 . append ( E . separator ( string = app_name , colspan = " 4 " , * * attrs ) )
2011-12-06 13:51:48 +00:00
for g in gs :
field_name = name_boolean_group ( g . id )
2012-04-03 08:27:43 +00:00
xml2 . append ( E . field ( name = field_name , * * attrs ) )
2012-03-08 17:43:04 +00:00
xml = E . field ( * ( xml1 + xml2 ) , name = " groups_id " , position = " replace " )
xml . addprevious ( etree . Comment ( " GENERATED AUTOMATICALLY BY GROUPS " ) )
xml_content = etree . tostring ( xml , pretty_print = True , xml_declaration = True , encoding = " utf-8 " )
view . write ( { ' arch ' : xml_content } )
2011-07-28 07:25:44 +00:00
return True
2011-12-15 15:40:48 +00:00
def get_application_groups ( self , cr , uid , domain = None , context = None ) :
return self . search ( cr , uid , domain or [ ] )
2011-12-06 09:10:11 +00:00
def get_groups_by_application ( self , cr , uid , context = None ) :
2011-12-09 09:27:00 +00:00
""" return all groups classified by application (module category), as a list of pairs:
2011-12-06 13:51:48 +00:00
[ ( app , kind , [ group , . . . ] ) , . . . ] ,
where app and group are browse records , and kind is either ' boolean ' or ' selection ' .
Applications are given in sequence order . If kind is ' selection ' , the groups are
2011-12-09 09:27:00 +00:00
given in reverse implication order .
2011-12-06 09:10:11 +00:00
"""
2011-12-06 13:51:48 +00:00
def linearized ( gs ) :
gs = set ( gs )
2011-12-06 16:20:20 +00:00
# determine sequence order: a group should appear after its implied groups
2011-12-06 13:51:48 +00:00
order = dict . fromkeys ( gs , 0 )
for g in gs :
for h in gs . intersection ( g . trans_implied_ids ) :
2011-12-06 11:05:15 +00:00
order [ h ] - = 1
2011-12-06 16:20:20 +00:00
# check whether order is total, i.e., sequence orders are distinct
2011-12-06 13:51:48 +00:00
if len ( set ( order . itervalues ( ) ) ) == len ( gs ) :
return sorted ( gs , key = lambda g : order [ g ] )
return None
2011-12-06 11:05:15 +00:00
2011-12-06 09:10:11 +00:00
# classify all groups by application
2011-12-15 15:40:48 +00:00
gids = self . get_application_groups ( cr , uid , context = context )
2011-12-06 09:10:11 +00:00
by_app , others = { } , [ ]
2011-12-09 12:48:21 +00:00
for g in self . browse ( cr , uid , gids , context ) :
2011-12-06 09:10:11 +00:00
if g . category_id :
by_app . setdefault ( g . category_id , [ ] ) . append ( g )
else :
others . append ( g )
# build the result
res = [ ]
apps = sorted ( by_app . iterkeys ( ) , key = lambda a : a . sequence or 0 )
for app in apps :
2011-12-06 13:51:48 +00:00
gs = linearized ( by_app [ app ] )
if gs :
res . append ( ( app , ' selection ' , gs ) )
else :
2011-12-09 09:27:00 +00:00
res . append ( ( app , ' boolean ' , by_app [ app ] ) )
2011-12-06 09:10:11 +00:00
if others :
2011-12-09 09:27:00 +00:00
res . append ( ( False , ' boolean ' , others ) )
2011-12-06 09:10:11 +00:00
return res
2011-07-29 14:04:00 +00:00
class users_view ( osv . osv ) :
2011-07-28 07:25:44 +00:00
_inherit = ' res.users '
def create ( self , cr , uid , values , context = None ) :
2011-12-09 13:46:58 +00:00
self . _set_reified_groups ( values )
2011-07-29 14:04:00 +00:00
return super ( users_view , self ) . create ( cr , uid , values , context )
2011-07-28 07:25:44 +00:00
def write ( self , cr , uid , ids , values , context = None ) :
2011-12-09 13:46:58 +00:00
self . _set_reified_groups ( values )
2011-07-29 14:04:00 +00:00
return super ( users_view , self ) . write ( cr , uid , ids , values , context )
2011-07-28 07:25:44 +00:00
2011-12-09 13:46:58 +00:00
def _set_reified_groups ( self , values ) :
2011-12-09 15:18:55 +00:00
""" reflect reified group fields in values[ ' groups_id ' ] """
if ' groups_id ' in values :
# groups are already given, ignore group fields
for f in filter ( is_reified_group , values . iterkeys ( ) ) :
del values [ f ]
return
2011-12-06 09:10:11 +00:00
add , remove = [ ] , [ ]
2011-12-09 13:46:58 +00:00
for f in values . keys ( ) :
if is_boolean_group ( f ) :
target = add if values . pop ( f ) else remove
target . append ( get_boolean_group ( f ) )
elif is_boolean_groups ( f ) :
if not values . pop ( f ) :
remove . extend ( get_boolean_groups ( f ) )
elif is_selection_groups ( f ) :
remove . extend ( get_selection_groups ( f ) )
selected = values . pop ( f )
2011-12-09 10:30:53 +00:00
if selected :
add . append ( selected )
2011-12-15 12:07:41 +00:00
# update values *only* if groups are being modified, otherwise
# we introduce spurious changes that might break the super.write() call.
if add or remove :
# remove groups in 'remove' and add groups in 'add'
values [ ' groups_id ' ] = [ ( 3 , id ) for id in remove ] + [ ( 4 , id ) for id in add ]
2011-12-09 13:46:58 +00:00
def default_get ( self , cr , uid , fields , context = None ) :
group_fields , fields = partition ( is_reified_group , fields )
2011-12-09 15:18:55 +00:00
fields1 = ( fields + [ ' groups_id ' ] ) if group_fields else fields
values = super ( users_view , self ) . default_get ( cr , uid , fields1 , context )
2011-12-09 13:46:58 +00:00
self . _get_reified_groups ( group_fields , values )
2013-02-26 16:26:48 +00:00
# add "default_groups_ref" inside the context to set default value for group_id with xml values
if ' groups_id ' in fields and isinstance ( context . get ( " default_groups_ref " ) , list ) :
groups = [ ]
2013-02-27 08:50:23 +00:00
ir_model_data = self . pool . get ( ' ir.model.data ' )
2013-02-26 16:26:48 +00:00
for group_xml_id in context [ " default_groups_ref " ] :
2013-02-27 08:50:23 +00:00
group_split = group_xml_id . split ( ' . ' )
2013-03-13 15:43:09 +00:00
if len ( group_split ) != 2 :
raise osv . except_osv ( _ ( ' Invalid context value ' ) , _ ( ' Invalid context default_groups_ref value (model.name_id) : " %s " ' ) % group_xml_id )
try :
2013-11-26 11:12:28 +00:00
temp , group_id = ir_model_data . get_object_reference ( cr , uid , group_split [ 0 ] , group_split [ 1 ] )
2013-03-13 15:43:09 +00:00
except ValueError :
group_id = False
2013-02-26 16:26:48 +00:00
groups + = [ group_id ]
values [ ' groups_id ' ] = groups
2011-12-09 13:46:58 +00:00
return values
2011-07-28 07:25:44 +00:00
2011-09-27 09:53:45 +00:00
def read ( self , cr , uid , ids , fields = None , context = None , load = ' _classic_read ' ) :
2013-11-26 11:12:28 +00:00
fields_get = fields if fields is not None else self . fields_get ( cr , uid , context = context ) . keys ( )
group_fields , _ = partition ( is_reified_group , fields_get )
inject_groups_id = group_fields and fields and ' groups_id ' not in fields
if inject_groups_id :
2012-02-10 10:59:55 +00:00
fields . append ( ' groups_id ' )
2011-12-09 13:46:58 +00:00
res = super ( users_view , self ) . read ( cr , uid , ids , fields , context = context , load = load )
2013-11-26 11:12:28 +00:00
2014-01-08 17:14:00 +00:00
if res and group_fields :
2013-11-26 11:12:28 +00:00
for values in ( res if isinstance ( res , list ) else [ res ] ) :
self . _get_reified_groups ( group_fields , values )
if inject_groups_id :
values . pop ( ' groups_id ' , None )
2011-11-14 21:26:27 +00:00
return res
2011-12-09 13:46:58 +00:00
def _get_reified_groups ( self , fields , values ) :
2011-12-09 15:18:55 +00:00
""" compute the given reified group fields from values[ ' groups_id ' ] """
2011-12-09 13:46:58 +00:00
gids = set ( values . get ( ' groups_id ' ) or [ ] )
for f in fields :
if is_boolean_group ( f ) :
values [ f ] = get_boolean_group ( f ) in gids
elif is_boolean_groups ( f ) :
values [ f ] = not gids . isdisjoint ( get_boolean_groups ( f ) )
elif is_selection_groups ( f ) :
selected = [ gid for gid in get_selection_groups ( f ) if gid in gids ]
values [ f ] = selected and selected [ - 1 ] or False
2011-07-28 07:25:44 +00:00
2011-12-06 09:10:11 +00:00
def fields_get ( self , cr , uid , allfields = None , context = None , write_access = True ) :
res = super ( users_view , self ) . fields_get ( cr , uid , allfields , context , write_access )
# add reified groups fields
2013-03-27 11:10:14 +00:00
for app , kind , gs in self . pool [ ' res.groups ' ] . get_groups_by_application ( cr , uid , context ) :
2011-12-06 13:51:48 +00:00
if kind == ' selection ' :
# selection group field
2012-09-06 06:49:12 +00:00
tips = [ ' %s : %s ' % ( g . name , g . comment ) for g in gs if g . comment ]
2011-12-06 13:51:48 +00:00
res [ name_selection_groups ( map ( int , gs ) ) ] = {
' type ' : ' selection ' ,
' string ' : app and app . name or _ ( ' Other ' ) ,
2011-12-19 08:34:58 +00:00
' selection ' : [ ( False , ' ' ) ] + [ ( g . id , g . name ) for g in gs ] ,
2011-12-06 13:51:48 +00:00
' help ' : ' \n ' . join ( tips ) ,
2013-06-07 09:30:57 +00:00
' exportable ' : False ,
2013-12-18 11:12:14 +00:00
' selectable ' : False ,
2011-12-06 13:51:48 +00:00
}
else :
# boolean group fields
for g in gs :
2011-12-06 11:05:15 +00:00
res [ name_boolean_group ( g . id ) ] = {
' type ' : ' boolean ' ,
' string ' : g . name ,
' help ' : g . comment ,
2013-06-07 09:30:57 +00:00
' exportable ' : False ,
2013-12-18 11:12:14 +00:00
' selectable ' : False ,
2011-12-06 11:05:15 +00:00
}
2011-07-28 07:25:44 +00:00
return res
2010-05-14 08:34:02 +00:00
2013-10-06 14:40:20 +00:00
#----------------------------------------------------------
# change password wizard
#----------------------------------------------------------
class change_password_wizard ( osv . TransientModel ) :
"""
A wizard to manage the change of users ' passwords
"""
_name = " change.password.wizard "
_description = " Change Password Wizard "
_columns = {
' user_ids ' : fields . one2many ( ' change.password.user ' , ' wizard_id ' , string = ' Users ' ) ,
}
def default_get ( self , cr , uid , fields , context = None ) :
if context == None :
context = { }
user_ids = context . get ( ' active_ids ' , [ ] )
wiz_id = context . get ( ' active_id ' , None )
res = [ ]
users = self . pool . get ( ' res.users ' ) . browse ( cr , uid , user_ids , context = context )
for user in users :
res . append ( ( 0 , 0 , {
' wizard_id ' : wiz_id ,
' user_id ' : user . id ,
' user_login ' : user . login ,
} ) )
return { ' user_ids ' : res }
def change_password_button ( self , cr , uid , id , context = None ) :
wizard = self . browse ( cr , uid , id , context = context ) [ 0 ]
2014-03-18 11:45:40 +00:00
need_reload = any ( uid == user . user_id . id for user in wizard . user_ids )
line_ids = [ user . id for user in wizard . user_ids ]
self . pool . get ( ' change.password.user ' ) . change_password_button ( cr , uid , line_ids , context = context )
2014-02-19 14:06:17 +00:00
# don't keep temporary password copies in the database longer than necessary
2014-03-18 11:45:40 +00:00
self . pool . get ( ' change.password.user ' ) . write ( cr , uid , line_ids , { ' new_passwd ' : False } , context = context )
if need_reload :
return {
' type ' : ' ir.actions.client ' ,
' tag ' : ' reload '
}
return { ' type ' : ' ir.actions.act_window_close ' }
2013-10-06 14:40:20 +00:00
class change_password_user ( osv . TransientModel ) :
"""
A model to configure users in the change password wizard
"""
_name = ' change.password.user '
_description = ' Change Password Wizard User '
_columns = {
' wizard_id ' : fields . many2one ( ' change.password.wizard ' , string = ' Wizard ' , required = True ) ,
' user_id ' : fields . many2one ( ' res.users ' , string = ' User ' , required = True ) ,
' user_login ' : fields . char ( ' User Login ' , readonly = True ) ,
' new_passwd ' : fields . char ( ' New Password ' ) ,
}
_defaults = {
' new_passwd ' : ' ' ,
}
def change_password_button ( self , cr , uid , ids , context = None ) :
for user in self . browse ( cr , uid , ids , context = context ) :
self . pool . get ( ' res.users ' ) . write ( cr , uid , user . user_id . id , { ' password ' : user . new_passwd } )
2011-06-16 11:54:20 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: