diff --git a/auditlog/models/rule.py b/auditlog/models/rule.py index 56c8ebc5c..38a635ba1 100644 --- a/auditlog/models/rule.py +++ b/auditlog/models/rule.py @@ -19,8 +19,7 @@ # ############################################################################## -from openerp import models, fields, api, modules -from openerp.tools.translate import _ +from openerp import models, fields, api, modules, _, SUPERUSER_ID FIELDS_BLACKLIST = [ 'id', 'create_uid', 'create_date', 'write_uid', 'write_date', @@ -40,8 +39,8 @@ class DictDiffer(object): """ def __init__(self, current_dict, past_dict): self.current_dict, self.past_dict = current_dict, past_dict - self.set_current = set(current_dict.keys()) - self.set_past = set(past_dict.keys()) + self.set_current = set(current_dict) + self.set_past = set(past_dict) self.intersect = self.set_current.intersection(self.set_past) def added(self): @@ -112,20 +111,26 @@ class auditlog_rule(models.Model): def _register_hook(self, cr, ids=None): """Get all rules and apply them to log method calls.""" super(auditlog_rule, self)._register_hook(cr) + if not hasattr(self.pool, '_auditlog_field_cache'): + self.pool._auditlog_field_cache = {} + if not hasattr(self.pool, '_auditlog_model_cache'): + self.pool._auditlog_model_cache = {} if ids is None: - ids = self.search(cr, 1, [('state', '=', 'subscribed')]) - return self._patch_methods(cr, 1, ids) + ids = self.search(cr, SUPERUSER_ID, [('state', '=', 'subscribed')]) + return self._patch_methods(cr, SUPERUSER_ID, ids) @api.multi def _patch_methods(self): """Patch ORM methods of models defined in rules to log their calls.""" updated = False + model_cache = self.pool._auditlog_model_cache for rule in self: if rule.state != 'subscribed': continue if not self.pool.get(rule.model_id.model): # ignore rules for models not loadable currently continue + model_cache[rule.model_id.model] = rule.model_id model_model = self.env[rule.model_id.model] # CRUD # -> create @@ -269,13 +274,16 @@ class auditlog_rule(models.Model): @api.multi def unlink(self, **kwargs): rule_model = self.env['auditlog.rule'] + old_values = dict( + (d['id'], d) for d in self.sudo().read(list(self._columns))) rule_model.sudo().create_logs( - self.env.uid, self._name, self.ids, 'unlink') + self.env.uid, self._name, self.ids, 'unlink', old_values) return unlink.origin(self, **kwargs) return unlink def create_logs(self, uid, res_model, res_ids, method, - old_values=None, new_values=None): + old_values=None, new_values=None, + additional_log_values=None): """Create logs. `old_values` and `new_values` are dictionnaries, e.g: {RES_ID: {'FIELD': VALUE, ...}} """ @@ -285,17 +293,17 @@ class auditlog_rule(models.Model): new_values = EMPTY_DICT log_model = self.env['auditlog.log'] ir_model = self.env['ir.model'] - model = ir_model.search([('model', '=', res_model)]) for res_id in res_ids: model_model = self.env[res_model] res_name = model_model.browse(res_id).name_get() vals = { 'name': res_name and res_name[0] and res_name[0][1] or False, - 'model_id': model.id, + 'model_id': self.pool._auditlog_model_cache[res_model].id, 'res_id': res_id, 'method': method, 'user_id': uid, } + vals.update(additional_log_values or {}) log = log_model.create(vals) diff = DictDiffer( new_values.get(res_id, EMPTY_DICT), @@ -304,17 +312,26 @@ class auditlog_rule(models.Model): log, diff.changed(), old_values, new_values) self._create_log_line_on_create(log, diff.added(), new_values) + def _get_field(self, model, field_name): + cache = self.pool._auditlog_field_cache + if field_name not in cache.get(model.model, {}): + cache.setdefault(model.model, {}) + cache[model.model][field_name] = self.env['ir.model.fields']\ + .search( + [ + ('model_id', '=', model.id), + ('name', '=', field_name), + ]) + return cache[model.model][field_name] + def _create_log_line_on_write( self, log, fields_list, old_values, new_values): """Log field updated on a 'write' operation.""" log_line_model = self.env['auditlog.log.line'] - ir_model_field = self.env['ir.model.fields'] for field_name in fields_list: if field_name in FIELDS_BLACKLIST: continue - field = ir_model_field.search( - [('model_id', '=', log.model_id.id), - ('name', '=', field_name)]) + field = self._get_field(log.model_id, field_name) log_vals = self._prepare_log_line_vals_on_write( log, field, old_values, new_values) log_line_model.create(log_vals) @@ -348,13 +365,10 @@ class auditlog_rule(models.Model): self, log, fields_list, new_values): """Log field filled on a 'create' operation.""" log_line_model = self.env['auditlog.log.line'] - ir_model_field = self.env['ir.model.fields'] for field_name in fields_list: if field_name in FIELDS_BLACKLIST: continue - field = ir_model_field.search( - [('model_id', '=', log.model_id.id), - ('name', '=', field_name)]) + field = self._get_field(log.model_id, field_name) log_vals = self._prepare_log_line_vals_on_create( log, field, new_values) log_line_model.create(log_vals) diff --git a/auditlog/tests/__init__.py b/auditlog/tests/__init__.py new file mode 100644 index 000000000..a688b88a7 --- /dev/null +++ b/auditlog/tests/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2015 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# 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 . +# +############################################################################## +from . import test_auditlog diff --git a/auditlog/tests/test_auditlog.py b/auditlog/tests/test_auditlog.py new file mode 100644 index 000000000..bc63dc1a2 --- /dev/null +++ b/auditlog/tests/test_auditlog.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2015 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# 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 . +# +############################################################################## +from openerp.tests.common import TransactionCase + + +class TestAuditlog(TransactionCase): + def test_LogCreation(self): + auditlog_log = self.env['auditlog.log'] + user_model_id = self.env.ref('base.model_res_users').id + self.env['auditlog.rule'].create({ + 'name': 'testrule for users', + 'model_id': user_model_id, + 'log_create': True, + 'log_write': True, + 'log_unlink': True, + 'state': 'subscribed', + }) + user = self.env['res.users'].create({ + 'login': 'testuser', + 'name': 'testuser', + }) + self.assertTrue(auditlog_log.search([ + ('model_id', '=', user_model_id), + ('method', '=', 'create'), + ('res_id', '=', user.id), + ])) + user.write({'name': 'Test User'}) + self.assertTrue(auditlog_log.search([ + ('model_id', '=', user_model_id), + ('method', '=', 'write'), + ('res_id', '=', user.id), + ])) + user.unlink() + self.assertTrue(auditlog_log.search([ + ('model_id', '=', user_model_id), + ('method', '=', 'unlink'), + ('res_id', '=', user.id), + ]))