Launchpad automatic translations update.
[odoo/odoo.git] / openerp / addons / base / ir / ir_rule.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from osv import fields, osv, expression
23 import time
24 from operator import itemgetter
25 from functools import partial
26 import tools
27 from tools.safe_eval import safe_eval as eval
28 from tools.misc import unquote as unquote
29 from openerp import SUPERUSER_ID
30
31 class ir_rule(osv.osv):
32     _name = 'ir.rule'
33     _order = 'name'
34     _MODES = ['read', 'write', 'create', 'unlink']
35
36     def _eval_context_for_combinations(self):
37         """Returns a dictionary to use as evaluation context for
38            ir.rule domains, when the goal is to obtain python lists
39            that are easier to parse and combine, but not to
40            actually execute them."""
41         return {'user': unquote('user'),
42                 'time': unquote('time')}
43
44     def _eval_context(self, cr, uid):
45         """Returns a dictionary to use as evaluation context for
46            ir.rule domains."""
47         return {'user': self.pool.get('res.users').browse(cr, 1, uid),
48                 'time':time}
49
50     def _domain_force_get(self, cr, uid, ids, field_name, arg, context=None):
51         res = {}
52         eval_context = self._eval_context(cr, uid)
53         for rule in self.browse(cr, uid, ids, context):
54             if rule.domain_force:
55                 res[rule.id] = expression.normalize(eval(rule.domain_force, eval_context))
56             else:
57                 res[rule.id] = []
58         return res
59
60     def _get_value(self, cr, uid, ids, field_name, arg, context=None):
61         res = {}
62         for rule in self.browse(cr, uid, ids, context):
63             if not rule.groups:
64                 res[rule.id] = True
65             else:
66                 res[rule.id] = False
67         return res
68
69     def _check_model_obj(self, cr, uid, ids, context=None):
70         return not any(self.pool.get(rule.model_id.model).is_transient() for rule in self.browse(cr, uid, ids, context))
71
72     _columns = {
73         'name': fields.char('Name', size=128, select=1),
74         'model_id': fields.many2one('ir.model', 'Object',select=1, required=True),
75         'global': fields.function(_get_value, string='Global', type='boolean', store=True, help="If no group is specified the rule is global and applied to everyone"),
76         'groups': fields.many2many('res.groups', 'rule_group_rel', 'rule_group_id', 'group_id', 'Groups'),
77         'domain_force': fields.text('Domain'),
78         'domain': fields.function(_domain_force_get, string='Domain', type='text'),
79         'perm_read': fields.boolean('Apply For Read'),
80         'perm_write': fields.boolean('Apply For Write'),
81         'perm_create': fields.boolean('Apply For Create'),
82         'perm_unlink': fields.boolean('Apply For Delete')
83     }
84
85     _order = 'model_id DESC'
86
87     _defaults = {
88         'perm_read': True,
89         'perm_write': True,
90         'perm_create': True,
91         'perm_unlink': True,
92         'global': True,
93     }
94     _sql_constraints = [
95         ('no_access_rights', 'CHECK (perm_read!=False or perm_write!=False or perm_create!=False or perm_unlink!=False)', 'Rule must have at least one checked access right !'),
96     ]
97     _constraints = [
98         (_check_model_obj, 'Rules are not supported for osv_memory objects !', ['model_id'])
99     ]
100
101     @tools.ormcache()
102     def _compute_domain(self, cr, uid, model_name, mode="read"):
103         if mode not in self._MODES:
104             raise ValueError('Invalid mode: %r' % (mode,))
105
106         if uid == SUPERUSER_ID:
107             return None
108         cr.execute("""SELECT r.id
109                 FROM ir_rule r
110                 JOIN ir_model m ON (r.model_id = m.id)
111                 WHERE m.model = %s
112                 AND r.perm_""" + mode + """
113                 AND (r.id IN (SELECT rule_group_id FROM rule_group_rel g_rel
114                             JOIN res_groups_users_rel u_rel ON (g_rel.group_id = u_rel.gid)
115                             WHERE u_rel.uid = %s) OR r.global)""", (model_name, uid))
116         rule_ids = [x[0] for x in cr.fetchall()]
117         if rule_ids:
118             # browse user as super-admin root to avoid access errors!
119             user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid)
120             global_domains = []                 # list of domains
121             group_domains = {}                  # map: group -> list of domains
122             for rule in self.browse(cr, SUPERUSER_ID, rule_ids):
123                 # read 'domain' as UID to have the correct eval context for the rule.
124                 rule_domain = self.read(cr, uid, rule.id, ['domain'])['domain']
125                 dom = expression.normalize(rule_domain)
126                 for group in rule.groups:
127                     if group in user.groups_id:
128                         group_domains.setdefault(group, []).append(dom)
129                 if not rule.groups:
130                     global_domains.append(dom)
131             # combine global domains and group domains
132             if group_domains:
133                 group_domain = expression.OR(map(expression.OR, group_domains.values()))
134             else:
135                 group_domain = []
136             domain = expression.AND(global_domains + [group_domain])
137             return domain
138         return []
139
140     def clear_cache(self, cr, uid):
141         self._compute_domain.clear_cache(self)
142
143     def domain_get(self, cr, uid, model_name, mode='read', context=None):
144         dom = self._compute_domain(cr, uid, model_name, mode)
145         if dom:
146             # _where_calc is called as superuser. This means that rules can
147             # involve objects on which the real uid has no acces rights.
148             # This means also there is no implicit restriction (e.g. an object
149             # references another object the user can't see).
150             query = self.pool.get(model_name)._where_calc(cr, 1, dom, active_test=False)
151             return query.where_clause, query.where_clause_params, query.tables
152         return [], [], ['"'+self.pool.get(model_name)._table+'"']
153
154     def unlink(self, cr, uid, ids, context=None):
155         res = super(ir_rule, self).unlink(cr, uid, ids, context=context)
156         self.clear_cache(cr, uid)
157         return res
158
159     def create(self, cr, uid, vals, context=None):
160         res = super(ir_rule, self).create(cr, uid, vals, context=context)
161         self.clear_cache(cr, uid)
162         return res
163
164     def write(self, cr, uid, ids, vals, context=None):
165         res = super(ir_rule, self).write(cr, uid, ids, vals, context=context)
166         self.clear_cache(cr,uid)
167         return res
168
169 ir_rule()
170
171 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
172