# -*- encoding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution # Copyright (C) 2010 Smile (). All Rights Reserved # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # ############################################################################## import logging from openerp import api, fields, models, SUPERUSER_ID, tools, _ from openerp.modules.registry import RegistryManager from audit_decorator import audit_decorator _logger = logging.getLogger(__package__) class AuditRule(models.Model): _name = 'audit.rule' _description = 'Audit Rule' name = fields.Char(size=32, required=True) active = fields.Boolean(default=True) log_create = fields.Boolean('Log Creation', default=True) log_write = fields.Boolean('Log Update', default=True) log_unlink = fields.Boolean('Log Deletion', default=True) state = fields.Selection([('draft', 'Draft'), ('done', 'Done')], 'Status', default='draft', readonly=True) model_id = fields.Many2one('ir.model', 'Object', required=True, help='Select object for which you want to generate log.', domain=[('model', '!=', 'audit.log')], readonly=True, states={'draft': [('readonly', False)]}) action_id = fields.Many2one('ir.actions.act_window', 'Client Action', readonly=True) values_id = fields.Many2one('ir.values', "Add in the 'More' menu", readonly=True) _sql_constraints = [ ('model_uniq', 'unique(model_id)', 'There is already a rule defined on this object.\n' 'You cannot define another: please edit the existing one.'), ] @api.one def _add_action(self): if not self.action_id: vals = { 'name': _('View audit logs'), 'res_model': 'audit.log', 'src_model': self.model_id.model, 'domain': "[('model_id','=', %s), ('res_id', '=', active_id)]" % self.model_id.id, } self.action_id = self.env['ir.actions.act_window'].create(vals) @api.one def _add_values(self): if not self.values_id: self.env['ir.model.data'].ir_set('action', 'client_action_relate', 'view_log_' + self.model_id.model, [self.model_id.model], 'ir.actions.act_window,%s' % self.action_id.id, replace=True, isobject=True, xml_id=False) values = self.env['ir.values'].search([('model', '=', self.model_id.model), ('value', '=', 'ir.actions.act_window,%s' % self.action_id.id)]) if values: self.values_id = values[0] @api.one def _activate(self): if self._context and \ self._context.get('activation_in_progress'): return self = self.with_context(activation_in_progress=True) self._add_action() self._add_values() @api.one def _deactivate(self): if self.values_id: self.values_id.unlink() if self.action_id: self.action_id.unlink() @api.multi def update_rule(self, force_deactivation=False): for rule in self: if rule.active and not force_deactivation: rule._activate() else: rule._deactivate() return True @tools.cache() def _check_audit_rule(self, cr): ids = self.search(cr, SUPERUSER_ID, []) return {rule.model_id.model: {method: rule.id for method in ('create', 'write', 'unlink') if getattr(rule, 'log_%s' % method)} for rule in self.browse(cr, SUPERUSER_ID, ids)} def _register_hook(self, cr, ids=None): updated = False if not ids: ids = self.search(cr, SUPERUSER_ID, []) for rule in self.browse(cr, SUPERUSER_ID, ids): model_obj = self.pool.get(rule.model_id.model) if not model_obj: continue if rule.active and not hasattr(model_obj, 'audit_rule'): for method in ('create', 'write', 'unlink'): model_obj._patch_method(method, audit_decorator()) model_obj.audit_rule = True updated = True if not rule.active and hasattr(model_obj, 'audit_rule'): for method_name in ('create', 'write', 'unlink'): method = getattr(model_obj, method_name) while hasattr(method, 'origin'): if method.__name__ == 'audit_wrapper': model_obj._revert_method(method_name) break method = method.origin del model_obj.audit_rule updated = True if updated: self.clear_caches() return updated @api.model @api.returns('self', lambda value: value.id) def create(self, vals): vals['state'] = 'done' rule = super(AuditRule, self).create(vals) rule.update_rule() if self._register_hook(rule.id): RegistryManager.signal_registry_change(self.env.cr.dbname) return rule @api.multi def write(self, vals): res = super(AuditRule, self).write(vals) self.update_rule() if self._register_hook(self._ids): RegistryManager.signal_registry_change(self.env.cr.dbname) return res @api.multi def unlink(self): self.update_rule(force_deactivation=True) return super(AuditRule, self).unlink() _ignored_fields = ['message_ids', 'message_last_post'] @classmethod def _format_data_to_log(cls, old_values, new_values): data = {} for age in ('old', 'new'): vals_list = old_values if age == 'old' else new_values if isinstance(vals_list, dict): vals_list = [vals_list] for vals in vals_list or []: for field in cls._ignored_fields: vals.pop(field, None) res_id = vals.pop('id') if vals: data.setdefault(res_id, {'old': {}, 'new': {}})[age] = vals return data @api.one def log(self, method, old_values=None, new_values=None): _logger.debug('Starting audit log') data = self._format_data_to_log(old_values, new_values) for res_id in data: self.env['audit.log'].sudo().create({ 'user_id': self._uid, 'model_id': self.sudo().model_id.id, 'method': method, 'res_id': res_id, 'data': repr(data[res_id]), }) return True