bugfix with menu permissions
[odoo/odoo.git] / bin / addons / base / ir / ir_ui_menu.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
5 #
6 # $Id$
7 #
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
13 # Service Company
14 #
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
19 #
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28 #
29 ##############################################################################
30
31 from osv import fields, osv
32 from osv.orm import browse_null, browse_record
33 import re
34 import tools
35
36 def one_in(setA, setB):
37     """Check the presence of an element of setA in setB
38     """
39     for x in setA:
40         if x in setB:
41             return True
42     return False
43
44 class many2many_unique(fields.many2many):
45     def set(self, cr, obj, id, name, values, user=None, context=None):
46         if not values:
47             return
48         val = values[:]
49         for act in values:
50             if act[0]==4:
51                 cr.execute('SELECT * FROM '+self._rel+' \
52                         WHERE '+self._id1+'=%d AND '+self._id2+'=%d', (id, act[1]))
53                 if cr.fetchall():
54                     val.remove(act)
55         return super(many2many_unique, self).set(cr, obj, id, name, val, user=user,
56                 context=context)
57
58
59 class ir_ui_menu(osv.osv):
60     _name = 'ir.ui.menu'
61     def search(self, cr, uid, args, offset=0, limit=2000, order=None,
62             context=None, count=False):
63         if context is None:
64             context = {}
65         ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order,
66                 context=context)
67         if uid==1:
68             return ids
69         user_groups = set(self.pool.get('res.users').browse(cr, uid, uid)['groups_id'] or [])
70         result = []
71         for menu in self.browse(cr, uid, ids):
72             restrict_to_groups = menu.groups_id
73             if not restrict_to_groups:
74                 if menu.action:
75                     # if the menu itself has no restrictions, we get the groups of 
76                     # the action of the menu
77                     try:
78                         m, oid = menu.action.split(',', 1)
79                         data = self.pool.get(m).browse(cr, uid, int(oid), context=context)
80                         if data and 'groups_id' in data:
81                             restrict_to_groups = data['groups_id']
82                     except:
83                         pass
84
85             if restrict_to_groups and not user_groups.intersection(restrict_to_groups):
86                 continue
87             result.append(menu.id)
88         return result
89
90     def _get_full_name(self, cr, uid, ids, name, args, context):
91         res = {}
92         for m in self.browse(cr, uid, ids, context=context):
93             res[m.id] = self._get_one_full_name(m)
94         return res
95
96     def _get_one_full_name(self, menu, level=6):
97         if level<=0:
98             return '...'
99         if menu.parent_id:
100             parent_path = self._get_one_full_name(menu.parent_id, level-1) + "/"
101         else:
102             parent_path = ''
103         return parent_path + menu.name
104
105     def copy(self, cr, uid, id, default=None, context=None):
106         ir_values_obj = self.pool.get('ir.values')
107         res = super(ir_ui_menu, self).copy(cr, uid, id, context=context)
108         datas=self.read(cr,uid,[res],['name'])[0]
109         rex=re.compile('\([0-9]+\)')
110         concat=rex.findall(datas['name'])
111         if concat:
112             next_num=eval(concat[0])+1
113             datas['name']=rex.sub(('(%d)'%next_num),datas['name'])
114         else:
115             datas['name']=datas['name']+'(1)'
116         self.write(cr,uid,[res],{'name':datas['name']})
117         ids = ir_values_obj.search(cr, uid, [
118             ('model', '=', 'ir.ui.menu'),
119             ('res_id', '=', id),
120             ])
121         for iv in ir_values_obj.browse(cr, uid, ids):
122             new_id = ir_values_obj.copy(cr, uid, iv.id,
123                     default={'res_id': res}, context=context)
124         return res
125
126     def _action(self, cursor, user, ids, name, arg, context=None):
127         res = {}
128         values_obj = self.pool.get('ir.values')
129         value_ids = values_obj.search(cursor, user, [
130             ('model', '=', self._name), ('key', '=', 'action'),
131             ('key2', '=', 'tree_but_open'), ('res_id', 'in', ids)],
132             context=context)
133         values_action = {}
134         for value in values_obj.browse(cursor, user, value_ids, context=context):
135             values_action[value.res_id] = value.value
136         for menu_id in ids:
137             res[menu_id] = values_action.get(menu_id, False)
138         return res
139
140     def _action_inv(self, cursor, user, menu_id, name, value, arg, context=None):
141         if context is None:
142             context = {}
143         ctx = context.copy()
144         if 'read_delta' in ctx:
145             del ctx['read_delta']
146         values_obj = self.pool.get('ir.values')
147         values_ids = values_obj.search(cursor, user, [
148             ('model', '=', self._name), ('key', '=', 'action'),
149             ('key2', '=', 'tree_but_open'), ('res_id', '=', menu_id)],
150             context=context)
151         if values_ids:
152             values_obj.write(cursor, user, values_ids[0], {'value': value},
153                     context=ctx)
154         else:
155             values_obj.create(cursor, user, {
156                 'name': 'Menuitem',
157                 'model': self._name,
158                 'value': value,
159                 'object': True,
160                 'key': 'action',
161                 'key2': 'tree_but_open',
162                 'res_id': menu_id,
163                 }, context=ctx)
164
165     def _get_icon_pict(self, cr, uid, ids, name, args, context):
166         res = {}
167         for m in self.browse(cr, uid, ids, context=context):
168             res[m.id] = ('stock', (m.icon,'ICON_SIZE_MENU'))
169         return res
170
171     def onchange_icon(self, cr, uid, ids, icon):
172         if not icon:
173             return {}
174         return {'type': {'icon_pict': 'picture'}, 'value': {'icon_pict': ('stock', (icon,'ICON_SIZE_MENU'))}}
175
176     _columns = {
177         'name': fields.char('Menu', size=64, required=True, translate=True),
178         'sequence': fields.integer('Sequence'),
179         'child_id' : fields.one2many('ir.ui.menu', 'parent_id','Child ids'),
180         'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', select=True),
181         'groups_id': many2many_unique('res.groups', 'ir_ui_menu_group_rel',
182             'menu_id', 'gid', 'Groups'),
183         'complete_name': fields.function(_get_full_name, method=True,
184             string='Complete Name', type='char', size=128),
185         'icon': fields.selection(tools.icons, 'Icon', size=64),
186         'icon_pict': fields.function(_get_icon_pict, method=True, type='picture'),
187         'action': fields.function(_action, fnct_inv=_action_inv,
188             method=True, type='reference', string='Action',
189             selection=[
190                 ('ir.actions.report.custom', 'ir.actions.report.custom'),
191                 ('ir.actions.report.xml', 'ir.actions.report.xml'),
192                 ('ir.actions.act_window', 'ir.actions.act_window'),
193                 ('ir.actions.wizard', 'ir.actions.wizard'),
194                                 ('ir.actions.url', 'ir.actions.act_url'),
195                 ]),
196     }
197     _defaults = {
198         'icon' : lambda *a: 'STOCK_OPEN',
199         'icon_pict': lambda *a: ('stock', ('STOCK_OPEN','ICON_SIZE_MENU')),
200         'sequence' : lambda *a: 10
201     }
202     _order = "sequence,id"
203 ir_ui_menu()
204
205
206
207 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
208