1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (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 General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
23 from osv import fields, osv
24 from osv.orm import browse_null, browse_record
28 def one_in(setA, setB):
29 """Check the presence of an element of setA in setB
36 class many2many_unique(fields.many2many):
37 def set(self, cr, obj, id, name, values, user=None, context=None):
43 cr.execute('SELECT * FROM '+self._rel+' \
44 WHERE '+self._id1+'=%s AND '+self._id2+'=%s', (id, act[1]))
47 return super(many2many_unique, self).set(cr, obj, id, name, val, user=user,
51 class ir_ui_menu(osv.osv):
54 def __init__(self, *args, **kwargs):
56 r = super(ir_ui_menu, self).__init__(*args, **kwargs)
57 self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache')
61 self.pool.get('ir.model.access').unregister_cache_clearing_method(self._name, 'clear_cache')
62 return super(ir_ui_menu, self).__del__()
64 def clear_cache(self):
65 # radical but this doesn't frequently happen
68 def search(self, cr, uid, args, offset=0, limit=2000, order=None,
69 context=None, count=False):
72 ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order, context=context, count=(count and uid==1))
81 modelaccess = self.pool.get('ir.model.access')
82 user_groups = set(self.pool.get('res.users').read(cr, 1, uid, ['groups_id'])['groups_id'])
84 for menu in self.browse(cr, uid, ids):
85 # this key works because user access rights are all based on user's groups (cfr ir_model_access.check)
86 key = (cr.dbname, menu.id, tuple(user_groups))
87 if key in self._cache:
89 result.append(menu.id)
92 self._cache[key] = False
94 restrict_to_groups = [g.id for g in menu.groups_id]
95 if not user_groups.intersection(restrict_to_groups):
97 result.append(menu.id)
98 self._cache[key] = True
102 # we check if the user has access to the action of the menu
103 m, oid = menu.action.split(',', 1)
104 data = self.pool.get(m).browse(cr, 1, int(oid))
107 model_field = { 'ir.actions.act_window': 'res_model',
108 'ir.actions.report.custom': 'model',
109 'ir.actions.report.xml': 'model',
110 'ir.actions.wizard': 'model',
111 'ir.actions.server': 'model_id',
114 field = model_field.get(m)
115 if field and data[field]:
116 if not modelaccess.check(cr, uid, data[field], raise_exception=False):
119 # if there is no action, it's a 'folder' menu
120 if not menu.child_id:
121 # not displayed if there is no children
124 result.append(menu.id)
125 self._cache[key] = True
131 def _get_full_name(self, cr, uid, ids, name, args, context):
133 for m in self.browse(cr, uid, ids, context=context):
134 res[m.id] = self._get_one_full_name(m)
137 def _get_one_full_name(self, menu, level=6):
141 parent_path = self._get_one_full_name(menu.parent_id, level-1) + "/"
144 return parent_path + menu.name
146 def write(self, *args, **kwargs):
148 return super(ir_ui_menu, self).write(*args, **kwargs)
150 def unlink(self, *args, **kwargs):
152 return super(ir_ui_menu, self).unlink(*args, **kwargs)
154 def copy(self, cr, uid, id, default=None, context=None):
155 ir_values_obj = self.pool.get('ir.values')
156 res = super(ir_ui_menu, self).copy(cr, uid, id, context=context)
157 datas=self.read(cr,uid,[res],['name'])[0]
158 rex=re.compile('\([0-9]+\)')
159 concat=rex.findall(datas['name'])
161 next_num=eval(concat[0])+1
162 datas['name']=rex.sub(('(%d)'%next_num),datas['name'])
164 datas['name']=datas['name']+'(1)'
165 self.write(cr,uid,[res],{'name':datas['name']})
166 ids = ir_values_obj.search(cr, uid, [
167 ('model', '=', 'ir.ui.menu'),
170 for iv in ir_values_obj.browse(cr, uid, ids):
171 new_id = ir_values_obj.copy(cr, uid, iv.id,
172 default={'res_id': res}, context=context)
175 def _action(self, cursor, user, ids, name, arg, context=None):
177 values_obj = self.pool.get('ir.values')
178 value_ids = values_obj.search(cursor, user, [
179 ('model', '=', self._name), ('key', '=', 'action'),
180 ('key2', '=', 'tree_but_open'), ('res_id', 'in', ids)],
183 for value in values_obj.browse(cursor, user, value_ids, context=context):
184 values_action[value.res_id] = value.value
186 res[menu_id] = values_action.get(menu_id, False)
189 def _action_inv(self, cursor, user, menu_id, name, value, arg, context=None):
193 if self.CONCURRENCY_CHECK_FIELD in ctx:
194 del ctx[self.CONCURRENCY_CHECK_FIELD]
195 values_obj = self.pool.get('ir.values')
196 values_ids = values_obj.search(cursor, user, [
197 ('model', '=', self._name), ('key', '=', 'action'),
198 ('key2', '=', 'tree_but_open'), ('res_id', '=', menu_id)],
201 values_obj.write(cursor, user, values_ids[0], {'value': value},
204 values_obj.create(cursor, user, {
210 'key2': 'tree_but_open',
214 def _get_icon_pict(self, cr, uid, ids, name, args, context):
216 for m in self.browse(cr, uid, ids, context=context):
217 res[m.id] = ('stock', (m.icon,'ICON_SIZE_MENU'))
220 def onchange_icon(self, cr, uid, ids, icon):
223 return {'type': {'icon_pict': 'picture'}, 'value': {'icon_pict': ('stock', (icon,'ICON_SIZE_MENU'))}}
226 'name': fields.char('Menu', size=64, required=True, translate=True),
227 'sequence': fields.integer('Sequence'),
228 'child_id' : fields.one2many('ir.ui.menu', 'parent_id','Child ids'),
229 'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', select=True),
230 'groups_id': many2many_unique('res.groups', 'ir_ui_menu_group_rel',
231 'menu_id', 'gid', 'Groups', help="If you put groups, the visibility of this menu will be based on these groups. "\
232 "If this field is empty, Open ERP will compute visibility based on the related object's read access."),
233 'complete_name': fields.function(_get_full_name, method=True,
234 string='Complete Name', type='char', size=128),
235 'icon': fields.selection(tools.icons, 'Icon', size=64),
236 'icon_pict': fields.function(_get_icon_pict, method=True, type='picture'),
237 'action': fields.function(_action, fnct_inv=_action_inv,
238 method=True, type='reference', string='Action',
240 ('ir.actions.report.custom', 'ir.actions.report.custom'),
241 ('ir.actions.report.xml', 'ir.actions.report.xml'),
242 ('ir.actions.act_window', 'ir.actions.act_window'),
243 ('ir.actions.wizard', 'ir.actions.wizard'),
244 ('ir.actions.url', 'ir.actions.url'),
248 'icon' : lambda *a: 'STOCK_OPEN',
249 'icon_pict': lambda *a: ('stock', ('STOCK_OPEN','ICON_SIZE_MENU')),
250 'sequence' : lambda *a: 10
252 _order = "sequence,id"
257 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: