1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 # Copyright (C) 2010-2011 OpenERP SA (<http://openerp.com>).
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as
10 # published by the Free Software Foundation, either version 3 of the
11 # License, or (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
27 import openerp.modules
28 from osv import fields, osv
29 from tools.translate import _
31 def one_in(setA, setB):
32 """Check the presence of an element of setA in setB
39 class ir_ui_menu(osv.osv):
42 def __init__(self, *args, **kwargs):
44 r = super(ir_ui_menu, self).__init__(*args, **kwargs)
45 self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache')
48 def clear_cache(self):
49 # radical but this doesn't frequently happen
52 def _filter_visible_menus(self, cr, uid, ids, context=None):
53 """Filters the give menu ids to only keep the menu items that should be
54 visible in the menu hierarchy of the current user.
55 Uses a cache for speeding up the computation.
57 modelaccess = self.pool.get('ir.model.access')
58 user_groups = set(self.pool.get('res.users').read(cr, 1, uid, ['groups_id'])['groups_id'])
60 for menu in self.browse(cr, uid, ids, context=context):
61 # this key works because user access rights are all based on user's groups (cfr ir_model_access.check)
62 key = (cr.dbname, menu.id, tuple(user_groups))
63 if key in self._cache:
65 result.append(menu.id)
66 #elif not menu.groups_id and not menu.action:
67 # result.append(menu.id)
70 self._cache[key] = False
72 restrict_to_groups = [g.id for g in menu.groups_id]
73 if not user_groups.intersection(restrict_to_groups):
75 #result.append(menu.id)
76 #self._cache[key] = True
80 # we check if the user has access to the action of the menu
83 model_field = { 'ir.actions.act_window': 'res_model',
84 'ir.actions.report.xml': 'model',
85 'ir.actions.wizard': 'model',
86 'ir.actions.server': 'model_id',
89 field = model_field.get(menu.action._name)
90 if field and data[field]:
91 if not modelaccess.check(cr, uid, data[field], 'read', False):
94 # if there is no action, it's a 'folder' menu
96 # not displayed if there is no children
99 result.append(menu.id)
100 self._cache[key] = True
103 def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
107 ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0,
108 limit=None, order=order, context=context, count=False)
115 # menu filtering is done only on main menu tree, not other menu lists
116 if context.get('ir.ui.menu.full_list'):
119 result = self._filter_visible_menus(cr, uid, ids, context=context)
122 result = result[long(offset):]
124 result = result[:long(limit)]
130 def _get_full_name(self, cr, uid, ids, name, args, context):
132 for m in self.browse(cr, uid, ids, context=context):
133 res[m.id] = self._get_one_full_name(m)
136 def _get_one_full_name(self, menu, level=6):
140 parent_path = self._get_one_full_name(menu.parent_id, level-1) + "/"
143 return parent_path + menu.name
145 def create(self, *args, **kwargs):
147 return super(ir_ui_menu, self).create(*args, **kwargs)
149 def write(self, *args, **kwargs):
151 return super(ir_ui_menu, self).write(*args, **kwargs)
153 def unlink(self, *args, **kwargs):
155 return super(ir_ui_menu, self).unlink(*args, **kwargs)
157 def copy(self, cr, uid, id, default=None, context=None):
158 ir_values_obj = self.pool.get('ir.values')
159 res = super(ir_ui_menu, self).copy(cr, uid, id, context=context)
160 datas=self.read(cr,uid,[res],['name'])[0]
161 rex=re.compile('\([0-9]+\)')
162 concat=rex.findall(datas['name'])
164 next_num=int(concat[0])+1
165 datas['name']=rex.sub(('(%d)'%next_num),datas['name'])
167 datas['name']=datas['name']+'(1)'
168 self.write(cr,uid,[res],{'name':datas['name']})
169 ids = ir_values_obj.search(cr, uid, [
170 ('model', '=', 'ir.ui.menu'),
173 for iv in ir_values_obj.browse(cr, uid, ids):
174 ir_values_obj.copy(cr, uid, iv.id, default={'res_id': res},
178 def _action(self, cursor, user, ids, name, arg, context=None):
180 ir_values_obj = self.pool.get('ir.values')
181 value_ids = ir_values_obj.search(cursor, user, [
182 ('model', '=', self._name), ('key', '=', 'action'),
183 ('key2', '=', 'tree_but_open'), ('res_id', 'in', ids)],
186 for value in ir_values_obj.browse(cursor, user, value_ids, context=context):
187 values_action[value.res_id] = value.value
189 res[menu_id] = values_action.get(menu_id, False)
192 def _action_inv(self, cursor, user, menu_id, name, value, arg, context=None):
196 if self.CONCURRENCY_CHECK_FIELD in ctx:
197 del ctx[self.CONCURRENCY_CHECK_FIELD]
198 ir_values_obj = self.pool.get('ir.values')
199 values_ids = ir_values_obj.search(cursor, user, [
200 ('model', '=', self._name), ('key', '=', 'action'),
201 ('key2', '=', 'tree_but_open'), ('res_id', '=', menu_id)],
204 ir_values_obj.write(cursor, user, values_ids, {'value': value},
207 ir_values_obj.create(cursor, user, {
212 'key2': 'tree_but_open',
216 def _get_icon_pict(self, cr, uid, ids, name, args, context):
218 for m in self.browse(cr, uid, ids, context=context):
219 res[m.id] = ('stock', (m.icon,'ICON_SIZE_MENU'))
222 def onchange_icon(self, cr, uid, ids, icon):
225 return {'type': {'icon_pict': 'picture'}, 'value': {'icon_pict': ('stock', (icon,'ICON_SIZE_MENU'))}}
227 def read_image(self, path):
230 path_info = path.split(',')
231 icon_path = openerp.modules.get_module_resource(path_info[0],path_info[1])
235 icon_file = tools.file_open(icon_path,'rb')
236 icon_image = base64.encodestring(icon_file.read())
241 def _get_image_icon(self, cr, uid, ids, names, args, context=None):
243 for menu in self.browse(cr, uid, ids, context=context):
244 res[menu.id] = r = {}
246 fn_src = fn[:-5] # remove _data
247 r[fn] = self.read_image(menu[fn_src])
252 'name': fields.char('Menu', size=64, required=True, translate=True),
253 'sequence': fields.integer('Sequence'),
254 'child_id' : fields.one2many('ir.ui.menu', 'parent_id','Child IDs'),
255 'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', select=True),
256 'groups_id': fields.many2many('res.groups', 'ir_ui_menu_group_rel',
257 'menu_id', 'gid', 'Groups', help="If you have groups, the visibility of this menu will be based on these groups. "\
258 "If this field is empty, OpenERP will compute visibility based on the related object's read access."),
259 'complete_name': fields.function(_get_full_name,
260 string='Complete Name', type='char', size=128),
261 'icon': fields.selection(tools.icons, 'Icon', size=64),
262 'icon_pict': fields.function(_get_icon_pict, type='char', size=32),
263 'web_icon': fields.char('Web Icon File', size=128),
264 'web_icon_hover':fields.char('Web Icon File (hover)', size=128),
265 'web_icon_data': fields.function(_get_image_icon, string='Web Icon Image', type='binary', readonly=True, store=True, multi='icon'),
266 'web_icon_hover_data':fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'),
267 'action': fields.function(_action, fnct_inv=_action_inv,
268 type='reference', string='Action',
270 ('ir.actions.report.xml', 'ir.actions.report.xml'),
271 ('ir.actions.act_window', 'ir.actions.act_window'),
272 ('ir.actions.wizard', 'ir.actions.wizard'),
273 ('ir.actions.url', 'ir.actions.url'),
274 ('ir.actions.server', 'ir.actions.server'),
275 ('ir.actions.client', 'ir.actions.client'),
279 def _rec_message(self, cr, uid, ids, context=None):
280 return _('Error ! You can not create recursive Menu.')
283 (osv.osv._check_recursion, _rec_message , ['parent_id'])
286 'icon' : 'STOCK_OPEN',
287 'icon_pict': ('stock', ('STOCK_OPEN','ICON_SIZE_MENU')),
290 _order = "sequence,id"
295 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: