ir.attachment: revise the access control code, let it be overriden
[odoo/odoo.git] / bin / addons / base / ir / ir_attachment.py
index 554110b..0811e3b 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 ##############################################################################
-#    
+#
 #    OpenERP, Open Source Management Solution
 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
 #
@@ -15,7 +15,7 @@
 #    GNU Affero General Public License for more details.
 #
 #    You should have received a copy of the GNU Affero General Public License
-#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
 
@@ -24,21 +24,34 @@ from osv.orm import except_orm
 import tools
 
 class ir_attachment(osv.osv):
-    def check(self, cr, uid, ids, mode, context=None):
-        if not ids: 
+    def check(self, cr, uid, ids, mode, context=None, values=None):
+        """Restricts the access to an ir.attachment, according to referred model
+        In the 'document' module, it is overriden to relax this hard rule, since
+        more complex ones apply there.
+        """
+        if not ids:
             return
         ima = self.pool.get('ir.model.access')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        cr.execute('select distinct res_model from ir_attachment where id in ('+','.join(map(str, ids))+')')
-        for obj in cr.fetchall():
-            if obj[0]:
-                ima.check(cr, uid, obj[0], mode, context=context)
+        res_ids = {}
+        if ids:
+            if isinstance(ids, (int, long)):
+                ids = [ids]
+            cr.execute('SELECT DISTINCT res_model, res_id FROM ir_attachment WHERE id = ANY (%s)', (ids,), self._debug)
+            for rmod, rid in cr.fetchall():
+                if not (rmod and rid):
+                    continue
+                res_ids.setdefault(rmod,[]).append(rid)
+        if values:
+            if 'res_model' in values and 'res_id' in values:
+                res_ids.setdefault(values['res_model'],[]).append(values['res_id'])
+        
+        for model, mids in res_ids.items():
+            self.pool.get(model).check_access_rule(cr, uid, mids, mode, context=context)
 
     def search(self, cr, uid, args, offset=0, limit=None, order=None,
             context=None, count=False):
-        ids = super(ir_attachment, self).search(cr, uid, args, offset=offset, 
-                                                limit=limit, order=order, 
+        ids = super(ir_attachment, self).search(cr, uid, args, offset=offset,
+                                                limit=limit, order=order,
                                                 context=context, count=False)
         if not ids:
             if count:
@@ -64,9 +77,9 @@ class ir_attachment(osv.osv):
         return super(ir_attachment, self).read(cr, uid, ids, fields_to_read, context, load)
 
     def write(self, cr, uid, ids, vals, context=None):
-        self.check(cr, uid, ids, 'write', context=context)
+        self.check(cr, uid, ids, 'write', context=context, values=vals)
         return super(ir_attachment, self).write(cr, uid, ids, vals, context)
-    
+
     def copy(self, cr, uid, id, default=None, context=None):
         self.check(cr, uid, [id], 'write', context=context)
         return super(ir_attachment, self).copy(cr, uid, id, default, context)
@@ -76,8 +89,7 @@ class ir_attachment(osv.osv):
         return super(ir_attachment, self).unlink(cr, uid, ids, context)
 
     def create(self, cr, uid, values, context=None):
-        if 'res_model' in values and values['res_model'] != '':
-            self.pool.get('ir.model.access').check(cr, uid, values['res_model'], 'create', context=context)
+        self.check(cr, uid, [], mode='create', context=context, values=values)
         return super(ir_attachment, self).create(cr, uid, values, context)
 
     def action_get(self, cr, uid, context=None):
@@ -86,35 +98,53 @@ class ir_attachment(osv.osv):
         res_id = dataobj.browse(cr, uid, data_id, context).res_id
         return self.pool.get('ir.actions.act_window').read(cr, uid, res_id, [], context)
 
-    def _get_preview(self, cr, uid, ids, name, arg, context=None):
-        result = {}
-        if context is None:
-            context = {}
-        ctx = context.copy()    
-        ctx['bin_size'] = False
-        for i in self.browse(cr, uid, ids, context=ctx):
-            result[i.id] = False
-            for format in ('png','jpg','jpeg','gif','bmp'):
-                if (i.datas_fname and i.datas_fname.lower() or '').endswith(format):
-                    result[i.id]= i.datas
-                    break
-        return result
+    def _name_get_resname(self, cr, uid, ids, object,method, context):
+        data = {}
+        for attachment in self.browse(cr, uid, ids, context=context):
+            model_object = attachment.res_model
+            res_id = attachment.res_id
+            if model_object and res_id:
+                model_pool = self.pool.get(model_object)
+                res = model_pool.name_get(cr,uid,[res_id],context)
+                data[attachment.id] = (res and res[0][1]) or False
+            else:
+                 data[attachment.id] = False
+        return data
 
     _name = 'ir.attachment'
     _columns = {
-        'name': fields.char('Attachment Name',size=64, required=True),
+        'name': fields.char('Attachment Name',size=256, required=True),
         'datas': fields.binary('Data'),
-        'preview': fields.function(_get_preview, type='binary', string='Image Preview', method=True),
-        'datas_fname': fields.char('Filename',size=64),
+        'datas_fname': fields.char('Filename',size=256),
         'description': fields.text('Description'),
-        # Not required due to the document module !
-        'res_model': fields.char('Resource Object',size=64, readonly=True),
-        'res_id': fields.integer('Resource ID', readonly=True),
-        'link': fields.char('Link', size=256),
+        'res_name': fields.function(_name_get_resname, type='char', size=128,
+                string='Resource Name', method=True, store=True),
+        'res_model': fields.char('Resource Object',size=64, readonly=True,
+                help="The database object this attachment will be attached to"),
+        'res_id': fields.integer('Resource ID', readonly=True,
+                help="The record id this is attached to"),
+        'url': fields.char('Url', size=512, oldname="link"),
+        'type': fields.selection(
+                [ ('url','URL'), ('binary','Binary'), ],
+                'Type', help="Binary File or external URL", required=True, change_default=True),
 
         'create_date': fields.datetime('Date Created', readonly=True),
-        'create_uid':  fields.many2one('res.users', 'Creator', readonly=True),
+        'create_uid':  fields.many2one('res.users', 'Owner', readonly=True),
+        'company_id': fields.many2one('res.company', 'Company', change_default=True),
     }
+    
+    _defaults = {
+        'type': 'binary',
+        'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'ir.attachment', context=c),
+    }
+
+    def _auto_init(self, cr, context=None):
+        super(ir_attachment, self)._auto_init(cr, context)
+        cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('ir_attachment_res_idx',))
+        if not cr.fetchone():
+            cr.execute('CREATE INDEX ir_attachment_res_idx ON ir_attachment (res_model, res_id)')
+            cr.commit()
+
 ir_attachment()