Launchpad automatic translations update.
[odoo/odoo.git] / openerp / addons / base / ir / ir_ui_menu.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
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>).
7 #
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.
12 #
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.
17 #
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/>.
20 #
21 ##############################################################################
22
23 import base64
24 import re
25
26 import tools
27 import openerp.modules
28 from osv import fields, osv
29 from tools.translate import _
30
31 def one_in(setA, setB):
32     """Check the presence of an element of setA in setB
33     """
34     for x in setA:
35         if x in setB:
36             return True
37     return False
38
39 class ir_ui_menu(osv.osv):
40     _name = 'ir.ui.menu'
41
42     def __init__(self, *args, **kwargs):
43         self._cache = {}
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')
46         return r
47
48     def clear_cache(self):
49         # radical but this doesn't frequently happen
50         self._cache = {}
51
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.
56         """
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'])
59         result = []
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:
64                 if self._cache[key]:
65                     result.append(menu.id)
66                 #elif not menu.groups_id and not menu.action:
67                 #    result.append(menu.id)
68                 continue
69
70             self._cache[key] = False
71             if menu.groups_id:
72                 restrict_to_groups = [g.id for g in menu.groups_id]
73                 if not user_groups.intersection(restrict_to_groups):
74                     continue
75                 #result.append(menu.id)
76                 #self._cache[key] = True
77                 #continue
78
79             if menu.action:
80                 # we check if the user has access to the action of the menu
81                 data = menu.action
82                 if data:
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',
87                                   }
88
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):
92                             continue
93             else:
94                 # if there is no action, it's a 'folder' menu
95                 if not menu.child_id:
96                     # not displayed if there is no children
97                     continue
98
99             result.append(menu.id)
100             self._cache[key] = True
101         return result
102
103     def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
104         if context is None:
105             context = {}
106
107         ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0,
108             limit=None, order=order, context=context, count=False)
109
110         if not ids:
111             if count:
112                 return 0
113             return []
114
115         # menu filtering is done only on main menu tree, not other menu lists
116         if context.get('ir.ui.menu.full_list'):
117             result = ids
118         else:
119             result = self._filter_visible_menus(cr, uid, ids, context=context)
120
121         if offset:
122             result = result[long(offset):]
123         if limit:
124             result = result[:long(limit)]
125
126         if count:
127             return len(result)
128         return result
129
130     def _get_full_name(self, cr, uid, ids, name, args, context):
131         res = {}
132         for m in self.browse(cr, uid, ids, context=context):
133             res[m.id] = self._get_one_full_name(m)
134         return res
135
136     def _get_one_full_name(self, menu, level=6):
137         if level<=0:
138             return '...'
139         if menu.parent_id:
140             parent_path = self._get_one_full_name(menu.parent_id, level-1) + "/"
141         else:
142             parent_path = ''
143         return parent_path + menu.name
144
145     def create(self, *args, **kwargs):
146         self.clear_cache()
147         return super(ir_ui_menu, self).create(*args, **kwargs)
148
149     def write(self, *args, **kwargs):
150         self.clear_cache()
151         return super(ir_ui_menu, self).write(*args, **kwargs)
152
153     def unlink(self, *args, **kwargs):
154         self.clear_cache()
155         return super(ir_ui_menu, self).unlink(*args, **kwargs)
156
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'])
163         if concat:
164             next_num=int(concat[0])+1
165             datas['name']=rex.sub(('(%d)'%next_num),datas['name'])
166         else:
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'),
171             ('res_id', '=', id),
172             ])
173         for iv in ir_values_obj.browse(cr, uid, ids):
174             ir_values_obj.copy(cr, uid, iv.id, default={'res_id': res},
175                                context=context)
176         return res
177
178     def _action(self, cursor, user, ids, name, arg, context=None):
179         res = {}
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)],
184             context=context)
185         values_action = {}
186         for value in ir_values_obj.browse(cursor, user, value_ids, context=context):
187             values_action[value.res_id] = value.value
188         for menu_id in ids:
189             res[menu_id] = values_action.get(menu_id, False)
190         return res
191
192     def _action_inv(self, cursor, user, menu_id, name, value, arg, context=None):
193         if context is None:
194             context = {}
195         ctx = context.copy()
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)],
202             context=context)
203         if values_ids:
204             ir_values_obj.write(cursor, user, values_ids, {'value': value},
205                     context=ctx)
206         else:
207             ir_values_obj.create(cursor, user, {
208                 'name': 'Menuitem',
209                 'model': self._name,
210                 'value': value,
211                 'key': 'action',
212                 'key2': 'tree_but_open',
213                 'res_id': menu_id,
214                 }, context=ctx)
215
216     def _get_icon_pict(self, cr, uid, ids, name, args, context):
217         res = {}
218         for m in self.browse(cr, uid, ids, context=context):
219             res[m.id] = ('stock', (m.icon,'ICON_SIZE_MENU'))
220         return res
221
222     def onchange_icon(self, cr, uid, ids, icon):
223         if not icon:
224             return {}
225         return {'type': {'icon_pict': 'picture'}, 'value': {'icon_pict': ('stock', (icon,'ICON_SIZE_MENU'))}}
226
227     def read_image(self, path):
228         if not path:
229             return False
230         path_info = path.split(',')
231         icon_path = openerp.modules.get_module_resource(path_info[0],path_info[1])
232         icon_image = False
233         if icon_path:
234             try:
235                 icon_file = tools.file_open(icon_path,'rb')
236                 icon_image = base64.encodestring(icon_file.read())
237             finally:
238                 icon_file.close()
239         return icon_image
240
241     def _get_image_icon(self, cr, uid, ids, names, args, context=None):
242         res = {}
243         for menu in self.browse(cr, uid, ids, context=context):
244             res[menu.id] = r = {}
245             for fn in names:
246                 fn_src = fn[:-5]    # remove _data
247                 r[fn] = self.read_image(menu[fn_src])
248
249         return res
250
251     _columns = {
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',
269             selection=[
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'),
276             ]),
277     }
278
279     def _rec_message(self, cr, uid, ids, context=None):
280         return _('Error ! You can not create recursive Menu.')
281
282     _constraints = [
283         (osv.osv._check_recursion, _rec_message , ['parent_id'])
284     ]
285     _defaults = {
286         'icon' : 'STOCK_OPEN',
287         'icon_pict': ('stock', ('STOCK_OPEN','ICON_SIZE_MENU')),
288         'sequence' : 10,
289     }
290     _order = "sequence,id"
291 ir_ui_menu()
292
293
294
295 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
296