diff --git a/bin/addons/base/ir/ir_attachment.py b/bin/addons/base/ir/ir_attachment.py index acf3fc3d575..b025dc1b55b 100644 --- a/bin/addons/base/ir/ir_attachment.py +++ b/bin/addons/base/ir/ir_attachment.py @@ -29,8 +29,72 @@ ############################################################################## from osv import fields,osv +from osv.orm import except_orm +import tools class ir_attachment(osv.osv): + + def check(self, cr, uid, ids, mode): + if not ids: + return + ima = self.pool.get('ir.model.access') + if isinstance(ids, (int, long)): + ids = [ids] + objs = self.browse(cr, uid, ids) or [] + for o in objs: + if o and o.res_model: + ima.check(cr, uid, o.res_model, mode) + + check = tools.cache()(check) + + def search(self, cr, uid, *args, **kwargs): + ids = super(ir_attachment, self).search(cr, uid, *args, **kwargs) + if not ids: + return [] + models = super(ir_attachment,self).read(cr, uid, ids, ['id', 'res_model']) + cache = {} + ima = self.pool.get('ir.model.access') + for m in models: + if m['res_model'] in cache: + if not cache[m['res_model']]: + ids.remove(m['id']) + continue + cache[m['res_model']] = ima.check(cr, uid, m['res_model'], 'read', raise_exception=False) + return ids + + def read(self, cr, uid, ids, *args, **kwargs): + self.check(cr, uid, ids, 'read') + return super(ir_attachment, self).read(cr, uid, ids, *args, **kwargs) + + def write(self, cr, uid, ids, *args, **kwargs): + self.check(cr, uid, ids, 'write') + return super(ir_attachment, self).write(cr, uid, ids, *args, **kwargs) + + def copy(self, cr, uid, id, *args, **kwargs): + self.check(cr, uid, [id], 'write') + return super(ir_attachment, self).copy(cr, uid, id, *args, **kwargs) + + def unlink(self, cr, uid, ids, *args, **kwargs): + self.check(cr, uid, ids, 'unlink') + return super(ir_attachment, self).unlink(cr, uid, ids, *args, **kwargs) + + def create(self, cr, uid, values, *args, **kwargs): + if 'res_model' in values and values['res_model'] != '': + self.pool.get('ir.model.access').check(cr, uid, values['res_model'], 'create') + return super(ir_attachment, self).create(cr, uid, values, *args, **kwargs) + + def clear_cache(self): + self.check() + + def __init__(self, *args, **kwargs): + r = super(ir_attachment, self).__init__(*args, **kwargs) + self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache') + return r + + def __del__(self): + self.pool.get('ir.model.access').unregister_cache_clearing_method(self._name, 'clear_cache') + return super(ir_attachment, self).__del__() + _name = 'ir.attachment' _columns = { 'name': fields.char('Attachment Name',size=64, required=True), diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py index 1fe8b61a1f1..55609e048b8 100644 --- a/bin/addons/base/ir/ir_model.py +++ b/bin/addons/base/ir/ir_model.py @@ -329,27 +329,42 @@ class ir_model_access(osv.osv): check = tools.cache()(check) + __cache_clearing_methods = [] + + def register_cache_clearing_method(self, model, method): + self.__cache_clearing_methods.append((model, method)) + + def unregister_cache_clearing_method(self, model, method): + try: + i = self.__cache_clearing_methods.index((model, method)) + del self.__cache_clearing_methods[i] + except ValueError: + pass + + def call_cache_clearing_methods(self): + for model, method in self.__cache_clearing_methods: + getattr(self.pool.get(model), method)() + # # Check rights on actions # def write(self, cr, uid, *args, **argv): - self.pool.get('ir.ui.menu').clear_cache() + self.call_cache_clearing_methods() res = super(ir_model_access, self).write(cr, uid, *args, **argv) - self.check() + self.check() # clear the cache of check function return res + def create(self, cr, uid, *args, **argv): res = super(ir_model_access, self).create(cr, uid, *args, **argv) self.check() return res + def unlink(self, cr, uid, *args, **argv): - self.pool.get('ir.ui.menu').clear_cache() + self.call_cache_clearing_methods() res = super(ir_model_access, self).unlink(cr, uid, *args, **argv) self.check() return res - def read(self, cr, uid, *args, **argv): - res = super(ir_model_access, self).read(cr, uid, *args, **argv) - self.check() - return res + ir_model_access() class ir_model_data(osv.osv): diff --git a/bin/addons/base/ir/ir_ui_menu.py b/bin/addons/base/ir/ir_ui_menu.py index f787db2e5d2..54d613cf1b5 100644 --- a/bin/addons/base/ir/ir_ui_menu.py +++ b/bin/addons/base/ir/ir_ui_menu.py @@ -61,7 +61,13 @@ class ir_ui_menu(osv.osv): def __init__(self, *args, **kwargs): self._cache = {} - return super(ir_ui_menu, self).__init__(*args, **kwargs) + r = super(ir_ui_menu, self).__init__(*args, **kwargs) + self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache') + return r + + def __del__(self): + self.pool.get('ir.model.access').unregister_cache_clearing_method(self._name, 'clear_cache') + return super(ir_ui_menu, self).__del__() def clear_cache(self): # radical but this doesn't frequently happen diff --git a/bin/tools/misc.py b/bin/tools/misc.py index 5f7678b8025..509c04ec5e8 100644 --- a/bin/tools/misc.py +++ b/bin/tools/misc.py @@ -589,6 +589,13 @@ class currency(float): # return str(display_value) +def is_hashable(h): + try: + hash(h) + return True + except TypeError: + return False + # # Use it as a decorator of the function you plan to cache # Timeout: 0 = no timeout, otherwise in seconds @@ -607,6 +614,11 @@ class cache(object): # Update named arguments with positional argument values kwargs.update(dict(zip(arg_names, args))) + for k in kwargs: + if isinstance(kwargs[k], (list, dict, set)): + kwargs[k] = tuple(kwargs[k]) + elif not is_hashable(kwargs[k]): + kwargs[k] = repr(kwargs[k]) kwargs = kwargs.items() kwargs.sort() @@ -621,7 +633,8 @@ class cache(object): return value # Work out new value, cache it and return it - # Should copy() this value to avoid futur modf of the cacle ? + # FIXME Should copy() this value to avoid futur modifications of the cache ? + # FIXME What about exceptions ? result = fn(self2,cr,**dict(kwargs)) self.cache[key] = (result, time.time())