ir.attachment: revise the access control code, let it be overriden
[odoo/odoo.git] / bin / addons / base / ir / ir_attachment.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
23 from osv.orm import except_orm
24 import tools
25
26 class ir_attachment(osv.osv):
27     def check(self, cr, uid, ids, mode, context=None, values=None):
28         """Restricts the access to an ir.attachment, according to referred model
29         In the 'document' module, it is overriden to relax this hard rule, since
30         more complex ones apply there.
31         """
32         if not ids:
33             return
34         ima = self.pool.get('ir.model.access')
35         res_ids = {}
36         if ids:
37             if isinstance(ids, (int, long)):
38                 ids = [ids]
39             cr.execute('SELECT DISTINCT res_model, res_id FROM ir_attachment WHERE id = ANY (%s)', (ids,), self._debug)
40             for rmod, rid in cr.fetchall():
41                 if not (rmod and rid):
42                     continue
43                 res_ids.setdefault(rmod,[]).append(rid)
44         if values:
45             if 'res_model' in values and 'res_id' in values:
46                 res_ids.setdefault(values['res_model'],[]).append(values['res_id'])
47         
48         for model, mids in res_ids.items():
49             self.pool.get(model).check_access_rule(cr, uid, mids, mode, context=context)
50
51     def search(self, cr, uid, args, offset=0, limit=None, order=None,
52             context=None, count=False):
53         ids = super(ir_attachment, self).search(cr, uid, args, offset=offset,
54                                                 limit=limit, order=order,
55                                                 context=context, count=False)
56         if not ids:
57             if count:
58                 return 0
59             return []
60         models = super(ir_attachment,self).read(cr, uid, ids, ['id', 'res_model'])
61         cache = {}
62         ima = self.pool.get('ir.model.access')
63         for m in models:
64             if m['res_model']:
65                 if m['res_model'] not in cache:
66                     cache[m['res_model']] = ima.check(cr, uid, m['res_model'], 'read',
67                                                       raise_exception=False, context=context)
68                 if not cache[m['res_model']]:
69                     ids.remove(m['id'])
70
71         if count:
72             return len(ids)
73         return ids
74
75     def read(self, cr, uid, ids, fields_to_read=None, context=None, load='_classic_read'):
76         self.check(cr, uid, ids, 'read', context=context)
77         return super(ir_attachment, self).read(cr, uid, ids, fields_to_read, context, load)
78
79     def write(self, cr, uid, ids, vals, context=None):
80         self.check(cr, uid, ids, 'write', context=context, values=vals)
81         return super(ir_attachment, self).write(cr, uid, ids, vals, context)
82
83     def copy(self, cr, uid, id, default=None, context=None):
84         self.check(cr, uid, [id], 'write', context=context)
85         return super(ir_attachment, self).copy(cr, uid, id, default, context)
86
87     def unlink(self, cr, uid, ids, context=None):
88         self.check(cr, uid, ids, 'unlink', context=context)
89         return super(ir_attachment, self).unlink(cr, uid, ids, context)
90
91     def create(self, cr, uid, values, context=None):
92         self.check(cr, uid, [], mode='create', context=context, values=values)
93         return super(ir_attachment, self).create(cr, uid, values, context)
94
95     def action_get(self, cr, uid, context=None):
96         dataobj = self.pool.get('ir.model.data')
97         data_id = dataobj._get_id(cr, 1, 'base', 'action_attachment')
98         res_id = dataobj.browse(cr, uid, data_id, context).res_id
99         return self.pool.get('ir.actions.act_window').read(cr, uid, res_id, [], context)
100
101     def _name_get_resname(self, cr, uid, ids, object,method, context):
102         data = {}
103         for attachment in self.browse(cr, uid, ids, context=context):
104             model_object = attachment.res_model
105             res_id = attachment.res_id
106             if model_object and res_id:
107                 model_pool = self.pool.get(model_object)
108                 res = model_pool.name_get(cr,uid,[res_id],context)
109                 data[attachment.id] = (res and res[0][1]) or False
110             else:
111                  data[attachment.id] = False
112         return data
113
114     _name = 'ir.attachment'
115     _columns = {
116         'name': fields.char('Attachment Name',size=256, required=True),
117         'datas': fields.binary('Data'),
118         'datas_fname': fields.char('Filename',size=256),
119         'description': fields.text('Description'),
120         'res_name': fields.function(_name_get_resname, type='char', size=128,
121                 string='Resource Name', method=True, store=True),
122         'res_model': fields.char('Resource Object',size=64, readonly=True,
123                 help="The database object this attachment will be attached to"),
124         'res_id': fields.integer('Resource ID', readonly=True,
125                 help="The record id this is attached to"),
126         'url': fields.char('Url', size=512, oldname="link"),
127         'type': fields.selection(
128                 [ ('url','URL'), ('binary','Binary'), ],
129                 'Type', help="Binary File or external URL", required=True, change_default=True),
130
131         'create_date': fields.datetime('Date Created', readonly=True),
132         'create_uid':  fields.many2one('res.users', 'Owner', readonly=True),
133         'company_id': fields.many2one('res.company', 'Company', change_default=True),
134     }
135     
136     _defaults = {
137         'type': 'binary',
138         'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'ir.attachment', context=c),
139     }
140
141     def _auto_init(self, cr, context=None):
142         super(ir_attachment, self)._auto_init(cr, context)
143         cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('ir_attachment_res_idx',))
144         if not cr.fetchone():
145             cr.execute('CREATE INDEX ir_attachment_res_idx ON ir_attachment (res_model, res_id)')
146             cr.commit()
147
148 ir_attachment()
149
150
151 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
152