2009-10-20 10:52:23 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
#
2009-09-30 11:20:35 +00:00
# OpenERP, Open Source Management Solution
2009-10-14 12:32:15 +00:00
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
2011-05-12 12:37:52 +00:00
# Copyright (C) 2010-2011 OpenERP SA (<http://openerp.com>).
2006-12-07 13:41:40 +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
2008-11-03 18:27:16 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2010-11-25 14:22:19 +00:00
import base64
2008-07-11 11:42:24 +00:00
import re
2012-01-25 11:58:23 +00:00
import threading
2012-08-22 16:14:07 +00:00
from tools . safe_eval import safe_eval as eval
2008-09-16 12:26:07 +00:00
import tools
2011-04-20 15:27:18 +00:00
import openerp . modules
2010-11-25 14:22:19 +00:00
from osv import fields , osv
2010-11-18 16:47:21 +00:00
from tools . translate import _
2012-08-31 13:53:09 +00:00
from openerp import SUPERUSER_ID
2006-12-07 13:41:40 +00:00
def one_in ( setA , setB ) :
2008-07-22 14:24:36 +00:00
""" Check the presence of an element of setA in setB
"""
for x in setA :
if x in setB :
return True
return False
2006-12-07 13:41:40 +00:00
class ir_ui_menu ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = ' ir.ui.menu '
2008-10-09 07:29:00 +00:00
def __init__ ( self , * args , * * kwargs ) :
2012-01-25 11:58:23 +00:00
self . cache_lock = threading . RLock ( )
self . clear_cache ( )
2008-10-16 18:28:16 +00:00
r = super ( ir_ui_menu , self ) . __init__ ( * args , * * kwargs )
self . pool . get ( ' ir.model.access ' ) . register_cache_clearing_method ( self . _name , ' clear_cache ' )
return r
2008-10-09 07:29:00 +00:00
def clear_cache ( self ) :
2012-01-25 11:58:23 +00:00
with self . cache_lock :
# radical but this doesn't frequently happen
self . _cache = { }
2008-10-09 07:29:00 +00:00
2010-12-10 16:17:08 +00:00
def _filter_visible_menus ( self , cr , uid , ids , context = None ) :
""" Filters the give menu ids to only keep the menu items that should be
visible in the menu hierarchy of the current user .
Uses a cache for speeding up the computation .
"""
2012-01-25 11:58:23 +00:00
with self . cache_lock :
modelaccess = self . pool . get ( ' ir.model.access ' )
2012-08-31 13:53:09 +00:00
user_groups = set ( self . pool . get ( ' res.users ' ) . read ( cr , SUPERUSER_ID , uid , [ ' groups_id ' ] ) [ ' groups_id ' ] )
2012-01-25 11:58:23 +00:00
result = [ ]
for menu in self . browse ( cr , uid , ids , context = context ) :
# this key works because user access rights are all based on user's groups (cfr ir_model_access.check)
key = ( cr . dbname , menu . id , tuple ( user_groups ) )
if key in self . _cache :
if self . _cache [ key ] :
result . append ( menu . id )
#elif not menu.groups_id and not menu.action:
# result.append(menu.id)
2008-10-09 07:29:00 +00:00
continue
2012-01-25 11:58:23 +00:00
self . _cache [ key ] = False
if menu . groups_id :
restrict_to_groups = [ g . id for g in menu . groups_id ]
if not user_groups . intersection ( restrict_to_groups ) :
continue
#result.append(menu.id)
#self._cache[key] = True
#continue
2008-10-09 07:29:00 +00:00
2012-01-25 11:58:23 +00:00
if menu . action :
# we check if the user has access to the action of the menu
data = menu . action
if data :
model_field = { ' ir.actions.act_window ' : ' res_model ' ,
' ir.actions.report.xml ' : ' model ' ,
' ir.actions.wizard ' : ' model ' ,
' ir.actions.server ' : ' model_id ' ,
}
field = model_field . get ( menu . action . _name )
if field and data [ field ] :
if not modelaccess . check ( cr , uid , data [ field ] , ' read ' , False ) :
continue
else :
# if there is no action, it's a 'folder' menu
if not menu . child_id :
# not displayed if there is no children
continue
2008-10-09 07:29:00 +00:00
2012-01-25 11:58:23 +00:00
result . append ( menu . id )
self . _cache [ key ] = True
return result
2010-12-10 16:17:08 +00:00
def search ( self , cr , uid , args , offset = 0 , limit = None , order = None , context = None , count = False ) :
if context is None :
context = { }
ids = super ( ir_ui_menu , self ) . search ( cr , uid , args , offset = 0 ,
limit = None , order = order , context = context , count = False )
if not ids :
if count :
return 0
return [ ]
# menu filtering is done only on main menu tree, not other menu lists
if context . get ( ' ir.ui.menu.full_list ' ) :
result = ids
else :
result = self . _filter_visible_menus ( cr , uid , ids , context = context )
2008-10-09 07:29:00 +00:00
2010-05-13 07:30:31 +00:00
if offset :
result = result [ long ( offset ) : ]
if limit :
result = result [ : long ( limit ) ]
2008-10-09 07:29:00 +00:00
if count :
return len ( result )
2008-07-22 14:24:36 +00:00
return result
2006-12-07 13:41:40 +00:00
2012-10-29 10:46:36 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
res = [ ]
for id in ids :
elmt = self . browse ( cr , uid , id , context = context )
res . append ( ( id , self . _get_one_full_name ( elmt ) ) )
return res
def _get_full_name ( self , cr , uid , ids , name = None , args = None , context = None ) :
if context == None :
context = { }
2008-07-22 14:24:36 +00:00
res = { }
2012-10-29 10:46:36 +00:00
for elmt in self . browse ( cr , uid , ids , context = context ) :
res [ elmt . id ] = self . _get_one_full_name ( elmt )
2008-07-22 14:24:36 +00:00
return res
2006-12-07 13:41:40 +00:00
2012-10-29 10:46:36 +00:00
def _get_one_full_name ( self , elmt , level = 6 ) :
2008-07-22 14:24:36 +00:00
if level < = 0 :
return ' ... '
2012-10-29 10:46:36 +00:00
if elmt . parent_id :
parent_path = self . _get_one_full_name ( elmt . parent_id , level - 1 ) + " / "
2008-07-22 14:24:36 +00:00
else :
parent_path = ' '
2012-10-29 10:46:36 +00:00
return parent_path + elmt . name
2006-12-07 13:41:40 +00:00
2010-12-10 16:17:08 +00:00
def create ( self , * args , * * kwargs ) :
self . clear_cache ( )
return super ( ir_ui_menu , self ) . create ( * args , * * kwargs )
2008-10-09 07:29:00 +00:00
def write ( self , * args , * * kwargs ) :
self . clear_cache ( )
return super ( ir_ui_menu , self ) . write ( * args , * * kwargs )
def unlink ( self , * args , * * kwargs ) :
self . clear_cache ( )
return super ( ir_ui_menu , self ) . unlink ( * args , * * kwargs )
2008-07-22 14:24:36 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
ir_values_obj = self . pool . get ( ' ir.values ' )
res = super ( ir_ui_menu , self ) . copy ( cr , uid , id , context = context )
datas = self . read ( cr , uid , [ res ] , [ ' name ' ] ) [ 0 ]
rex = re . compile ( ' \ ([0-9]+ \ ) ' )
concat = rex . findall ( datas [ ' name ' ] )
if concat :
2010-09-02 10:41:07 +00:00
next_num = int ( concat [ 0 ] ) + 1
2008-07-22 14:24:36 +00:00
datas [ ' name ' ] = rex . sub ( ( ' ( %d ) ' % next_num ) , datas [ ' name ' ] )
else :
datas [ ' name ' ] = datas [ ' name ' ] + ' (1) '
self . write ( cr , uid , [ res ] , { ' name ' : datas [ ' name ' ] } )
ids = ir_values_obj . search ( cr , uid , [
( ' model ' , ' = ' , ' ir.ui.menu ' ) ,
( ' res_id ' , ' = ' , id ) ,
] )
for iv in ir_values_obj . browse ( cr , uid , ids ) :
2010-02-04 17:24:36 +00:00
ir_values_obj . copy ( cr , uid , iv . id , default = { ' res_id ' : res } ,
context = context )
2008-07-22 14:24:36 +00:00
return res
2006-12-07 13:41:40 +00:00
2008-07-22 14:24:36 +00:00
def _action ( self , cursor , user , ids , name , arg , context = None ) :
res = { }
2011-05-16 15:05:34 +00:00
ir_values_obj = self . pool . get ( ' ir.values ' )
value_ids = ir_values_obj . search ( cursor , user , [
2008-07-22 14:24:36 +00:00
( ' model ' , ' = ' , self . _name ) , ( ' key ' , ' = ' , ' action ' ) ,
( ' key2 ' , ' = ' , ' tree_but_open ' ) , ( ' res_id ' , ' in ' , ids ) ] ,
context = context )
values_action = { }
2011-05-16 15:05:34 +00:00
for value in ir_values_obj . browse ( cursor , user , value_ids , context = context ) :
2008-07-22 14:24:36 +00:00
values_action [ value . res_id ] = value . value
for menu_id in ids :
res [ menu_id ] = values_action . get ( menu_id , False )
return res
2007-10-19 12:14:58 +00:00
2008-07-22 14:24:36 +00:00
def _action_inv ( self , cursor , user , menu_id , name , value , arg , context = None ) :
if context is None :
context = { }
ctx = context . copy ( )
2009-01-15 11:57:18 +00:00
if self . CONCURRENCY_CHECK_FIELD in ctx :
del ctx [ self . CONCURRENCY_CHECK_FIELD ]
2011-05-16 15:05:34 +00:00
ir_values_obj = self . pool . get ( ' ir.values ' )
values_ids = ir_values_obj . search ( cursor , user , [
2008-07-22 14:24:36 +00:00
( ' model ' , ' = ' , self . _name ) , ( ' key ' , ' = ' , ' action ' ) ,
( ' key2 ' , ' = ' , ' tree_but_open ' ) , ( ' res_id ' , ' = ' , menu_id ) ] ,
context = context )
2012-02-10 08:26:37 +00:00
if value and values_ids :
ir_values_obj . write ( cursor , user , values_ids , { ' value ' : value } , context = ctx )
elif value :
# no values_ids, create binding
2011-05-16 15:05:34 +00:00
ir_values_obj . create ( cursor , user , {
2008-07-22 14:24:36 +00:00
' name ' : ' Menuitem ' ,
' model ' : self . _name ,
' value ' : value ,
' key ' : ' action ' ,
' key2 ' : ' tree_but_open ' ,
' res_id ' : menu_id ,
} , context = ctx )
2012-02-10 08:26:37 +00:00
elif values_ids :
# value is False, remove existing binding
ir_values_obj . unlink ( cursor , user , values_ids , context = ctx )
2008-09-18 14:02:05 +00:00
2008-07-25 10:26:00 +00:00
def _get_icon_pict ( self , cr , uid , ids , name , args , context ) :
res = { }
for m in self . browse ( cr , uid , ids , context = context ) :
res [ m . id ] = ( ' stock ' , ( m . icon , ' ICON_SIZE_MENU ' ) )
return res
2008-09-16 12:26:07 +00:00
2008-07-25 10:26:00 +00:00
def onchange_icon ( self , cr , uid , ids , icon ) :
if not icon :
return { }
return { ' type ' : { ' icon_pict ' : ' picture ' } , ' value ' : { ' icon_pict ' : ( ' stock ' , ( icon , ' ICON_SIZE_MENU ' ) ) } }
2007-10-19 12:14:58 +00:00
2010-11-24 10:49:36 +00:00
def read_image ( self , path ) :
2011-05-12 12:37:52 +00:00
if not path :
return False
2010-11-25 10:50:02 +00:00
path_info = path . split ( ' , ' )
2011-04-20 15:27:18 +00:00
icon_path = openerp . modules . get_module_resource ( path_info [ 0 ] , path_info [ 1 ] )
2011-01-04 04:34:15 +00:00
icon_image = False
2010-12-31 09:32:40 +00:00
if icon_path :
2011-01-06 17:38:28 +00:00
try :
icon_file = tools . file_open ( icon_path , ' rb ' )
icon_image = base64 . encodestring ( icon_file . read ( ) )
finally :
icon_file . close ( )
2010-12-31 09:32:40 +00:00
return icon_image
2010-11-24 10:49:36 +00:00
2011-05-12 12:37:52 +00:00
def _get_image_icon ( self , cr , uid , ids , names , args , context = None ) :
2010-11-24 10:49:36 +00:00
res = { }
for menu in self . browse ( cr , uid , ids , context = context ) :
2011-05-12 12:37:52 +00:00
res [ menu . id ] = r = { }
for fn in names :
fn_src = fn [ : - 5 ] # remove _data
r [ fn ] = self . read_image ( menu [ fn_src ] )
2010-11-24 10:49:36 +00:00
return res
2010-07-19 11:34:29 +00:00
2012-03-28 16:35:49 +00:00
def _get_needaction ( self , cr , uid , ids , field_names , args , context = None ) :
2012-04-18 09:07:35 +00:00
res = { }
2012-03-28 16:35:49 +00:00
for menu in self . browse ( cr , uid , ids , context = context ) :
2012-08-28 07:53:21 +00:00
res [ menu . id ] = {
' needaction_enabled ' : False ,
' needaction_counter ' : False ,
}
2012-08-22 13:34:06 +00:00
if menu . action and menu . action . type in ( ' ir.actions.act_window ' , ' ir.actions.client ' ) and menu . action . res_model :
2012-08-19 14:59:28 +00:00
obj = self . pool . get ( menu . action . res_model )
2012-10-05 10:20:03 +00:00
if obj and obj . _needaction :
2012-08-23 14:35:25 +00:00
if menu . action . type == ' ir.actions.act_window ' :
dom = menu . action . domain and eval ( menu . action . domain , { ' uid ' : uid } ) or [ ]
else :
dom = eval ( menu . action . params_store or ' {} ' , { ' uid ' : uid } ) . get ( ' domain ' )
2012-08-19 14:59:28 +00:00
res [ menu . id ] [ ' needaction_enabled ' ] = obj . _needaction
2012-08-23 14:35:25 +00:00
res [ menu . id ] [ ' needaction_counter ' ] = obj . _needaction_count ( cr , uid , dom , context = context )
2012-03-28 16:35:49 +00:00
return res
2012-08-19 14:59:28 +00:00
2008-07-22 14:24:36 +00:00
_columns = {
' name ' : fields . char ( ' Menu ' , size = 64 , required = True , translate = True ) ,
' sequence ' : fields . integer ( ' Sequence ' ) ,
2009-01-26 17:40:29 +00:00
' child_id ' : fields . one2many ( ' ir.ui.menu ' , ' parent_id ' , ' Child IDs ' ) ,
2008-07-22 14:24:36 +00:00
' parent_id ' : fields . many2one ( ' ir.ui.menu ' , ' Parent Menu ' , select = True ) ,
2010-12-19 20:07:51 +00:00
' groups_id ' : fields . many2many ( ' res.groups ' , ' ir_ui_menu_group_rel ' ,
2009-01-26 17:40:29 +00:00
' menu_id ' , ' gid ' , ' Groups ' , help = " If you have groups, the visibility of this menu will be based on these groups. " \
2010-08-10 11:22:58 +00:00
" If this field is empty, OpenERP will compute visibility based on the related object ' s read access. " ) ,
2012-10-29 10:46:36 +00:00
' complete_name ' : fields . function ( _get_full_name ,
2012-06-04 18:30:55 +00:00
string = ' Full Path ' , type = ' char ' , size = 128 ) ,
2008-09-16 12:26:07 +00:00
' icon ' : fields . selection ( tools . icons , ' Icon ' , size = 64 ) ,
2012-01-04 13:30:27 +00:00
' icon_pict ' : fields . function ( _get_icon_pict , type = ' char ' , size = 32 ) ,
2010-12-29 09:47:54 +00:00
' web_icon ' : fields . char ( ' Web Icon File ' , size = 128 ) ,
' web_icon_hover ' : fields . char ( ' Web Icon File (hover) ' , size = 128 ) ,
2012-01-04 13:30:27 +00:00
' web_icon_data ' : fields . function ( _get_image_icon , string = ' Web Icon Image ' , type = ' binary ' , readonly = True , store = True , multi = ' icon ' ) ,
' web_icon_hover_data ' : fields . function ( _get_image_icon , string = ' Web Icon Image (hover) ' , type = ' binary ' , readonly = True , store = True , multi = ' icon ' ) ,
2012-04-02 08:18:26 +00:00
' needaction_enabled ' : fields . function ( _get_needaction , string = ' Target model uses the need action mechanism ' , type = ' boolean ' , help = ' If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false. ' , multi = ' _get_needaction ' ) ,
' needaction_counter ' : fields . function ( _get_needaction , string = ' Number of actions the user has to perform ' , type = ' integer ' , help = ' If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform. ' , multi = ' _get_needaction ' ) ,
2008-07-22 14:24:36 +00:00
' action ' : fields . function ( _action , fnct_inv = _action_inv ,
2012-01-04 13:30:27 +00:00
type = ' reference ' , string = ' Action ' ,
2008-07-22 14:24:36 +00:00
selection = [
( ' ir.actions.report.xml ' , ' ir.actions.report.xml ' ) ,
( ' ir.actions.act_window ' , ' ir.actions.act_window ' ) ,
( ' ir.actions.wizard ' , ' ir.actions.wizard ' ) ,
2012-06-08 10:17:10 +00:00
( ' ir.actions.act_url ' , ' ir.actions.act_url ' ) ,
2010-02-04 17:24:36 +00:00
( ' ir.actions.server ' , ' ir.actions.server ' ) ,
2011-07-01 13:05:17 +00:00
( ' ir.actions.client ' , ' ir.actions.client ' ) ,
2008-10-30 07:56:24 +00:00
] ) ,
2008-07-22 14:24:36 +00:00
}
2011-01-04 04:34:15 +00:00
2010-11-18 16:47:21 +00:00
def _rec_message ( self , cr , uid , ids , context = None ) :
return _ ( ' Error ! You can not create recursive Menu. ' )
2010-07-19 11:34:29 +00:00
_constraints = [
2010-12-09 10:57:33 +00:00
( osv . osv . _check_recursion , _rec_message , [ ' parent_id ' ] )
2010-07-19 11:34:29 +00:00
]
2008-07-22 14:24:36 +00:00
_defaults = {
2010-11-23 15:25:51 +00:00
' icon ' : ' STOCK_OPEN ' ,
' icon_pict ' : ( ' stock ' , ( ' STOCK_OPEN ' , ' ICON_SIZE_MENU ' ) ) ,
' sequence ' : 10 ,
2008-07-22 14:24:36 +00:00
}
_order = " sequence,id "
2008-07-23 15:01:27 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: