[ADD] Module 'auditlog' - A substitute to the deprecated 'audittrail' module
parent
b0f6a9445b
commit
65517c4bcf
|
@ -0,0 +1 @@
|
|||
Sebastien Alix <sebastien.alix@osiell.com>
|
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2013 ABF OSIELL (<http://osiell.com>).
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from . import models
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,47 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2013 ABF OSIELL (<http://osiell.com>).
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
{
|
||||
'name': "Audit Log",
|
||||
'description': """
|
||||
Track every user operation on all the objects of the system.
|
||||
============================================================
|
||||
|
||||
The administrator can subscribe to rules for create, read, write and delete on
|
||||
models and can check logs.
|
||||
""",
|
||||
'version': "1.0",
|
||||
'author': "ABF OSIELL",
|
||||
'website': "http://www.osiell.com",
|
||||
'category': "Tools",
|
||||
'depends': [
|
||||
'base',
|
||||
],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/auditlog_view.xml',
|
||||
],
|
||||
'application': True,
|
||||
'installable': True,
|
||||
'active': False,
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,301 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * auditlog
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 8.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-22 09:51+0000\n"
|
||||
"PO-Revision-Date: 2015-01-22 09:51+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.rule,action_id:0
|
||||
msgid "Action"
|
||||
msgstr "Action"
|
||||
|
||||
#. module: auditlog
|
||||
#: model:ir.ui.menu,name:auditlog.menu_audit
|
||||
msgid "Audit"
|
||||
msgstr "Audit"
|
||||
|
||||
#. module: auditlog
|
||||
#: model:ir.model,name:auditlog.model_auditlog_log
|
||||
msgid "Auditlog - Log"
|
||||
msgstr "Auditlog - Log"
|
||||
|
||||
#. module: auditlog
|
||||
#: model:ir.model,name:auditlog.model_auditlog_log_line
|
||||
msgid "Auditlog - Log details (fields updated)"
|
||||
msgstr "Auditlog - Détails (champs modifiés)"
|
||||
|
||||
#. module: auditlog
|
||||
#: model:ir.model,name:auditlog.model_auditlog_rule
|
||||
msgid "Auditlog - Rule"
|
||||
msgstr "Auditlog - Règle"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log,create_uid:0
|
||||
#: field:auditlog.log.line,create_uid:0
|
||||
#: field:auditlog.rule,create_uid:0
|
||||
msgid "Created by"
|
||||
msgstr ""
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log,create_date:0
|
||||
#: field:auditlog.log.line,create_date:0
|
||||
#: field:auditlog.rule,create_date:0
|
||||
msgid "Created on"
|
||||
msgstr ""
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_search
|
||||
#: field:auditlog.log,timestamp:0
|
||||
msgid "Date"
|
||||
msgstr "Date"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log.line,field_description:0
|
||||
msgid "Description"
|
||||
msgstr "Description"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.rule:auditlog.view_auditlog_rule_search
|
||||
#: selection:auditlog.rule,state:0
|
||||
msgid "Draft"
|
||||
msgstr "Brouillon"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log.line,field_id:0
|
||||
msgid "Field"
|
||||
msgstr "Champ"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_form
|
||||
#: field:auditlog.log,line_ids:0
|
||||
msgid "Fields updated"
|
||||
msgstr "Champs modifiés"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_search
|
||||
#: view:auditlog.rule:auditlog.view_auditlog_rule_search
|
||||
msgid "Group By..."
|
||||
msgstr "Grouper par..."
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log,id:0
|
||||
#: field:auditlog.log.line,id:0
|
||||
#: field:auditlog.rule,id:0
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log,write_uid:0
|
||||
#: field:auditlog.log.line,write_uid:0
|
||||
#: field:auditlog.rule,write_uid:0
|
||||
msgid "Last Updated by"
|
||||
msgstr ""
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log,write_date:0
|
||||
#: field:auditlog.log.line,write_date:0
|
||||
#: field:auditlog.rule,write_date:0
|
||||
msgid "Last Updated on"
|
||||
msgstr ""
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_form
|
||||
#: field:auditlog.log.line,log_id:0
|
||||
msgid "Log"
|
||||
msgstr "Log"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_form
|
||||
msgid "Log - Field updated"
|
||||
msgstr "Log - Champs modifiés"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.rule,log_create:0
|
||||
msgid "Log Creates"
|
||||
msgstr "Enregistrer les créations"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.rule,log_unlink:0
|
||||
msgid "Log Deletes"
|
||||
msgstr "Enregistrer les suppressions"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.rule,log_read:0
|
||||
msgid "Log Reads"
|
||||
msgstr "Enregistrer les lectures"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.rule,log_write:0
|
||||
msgid "Log Writes"
|
||||
msgstr "Enregistrer les écritures"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_search
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_tree
|
||||
#: model:ir.actions.act_window,name:auditlog.action_auditlog_log_tree
|
||||
#: model:ir.ui.menu,name:auditlog.menu_audit_logs
|
||||
msgid "Logs"
|
||||
msgstr "Journaux"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log,method:0
|
||||
msgid "Method"
|
||||
msgstr "Méthode"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_search
|
||||
#: field:auditlog.log,model_id:0
|
||||
#: field:auditlog.rule,model_id:0
|
||||
msgid "Model"
|
||||
msgstr "Modèle"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.rule,name:0
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log.line,new_value:0
|
||||
msgid "New Value"
|
||||
msgstr "Nouvelle valeur"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log.line,new_value_text:0
|
||||
msgid "New value Text"
|
||||
msgstr "Nouvelle valeur texte"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log.line,old_value:0
|
||||
msgid "Old Value"
|
||||
msgstr "Ancienne valeur"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log.line,old_value_text:0
|
||||
msgid "Old value Text"
|
||||
msgstr "Ancienne valeur texte"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_search
|
||||
#: field:auditlog.log,res_id:0
|
||||
msgid "Resource ID"
|
||||
msgstr "ID de l'enregistrement"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log,name:0
|
||||
msgid "Resource Name"
|
||||
msgstr "Nom de l'enregistrement"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.rule:auditlog.view_auditlog_rule_form
|
||||
msgid "Rule"
|
||||
msgstr "Règle"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.rule:auditlog.view_auditlog_rule_search
|
||||
#: view:auditlog.rule:auditlog.view_auditlog_rule_tree
|
||||
#: model:ir.actions.act_window,name:auditlog.action_auditlog_rule_tree
|
||||
#: model:ir.ui.menu,name:auditlog.menu_action_auditlog_rule_tree
|
||||
msgid "Rules"
|
||||
msgstr "Règles"
|
||||
|
||||
#. module: auditlog
|
||||
#: help:auditlog.rule,model_id:0
|
||||
msgid "Select model for which you want to generate log."
|
||||
msgstr "Sélectionnez le modèle pour lequel vous voulez générer un historique."
|
||||
|
||||
#. module: auditlog
|
||||
#: help:auditlog.rule,log_create:0
|
||||
msgid "Select this if you want to keep track of creation on any record of the model of this rule"
|
||||
msgstr ""
|
||||
"Cochez cette case si vous voulez garder une trace de la création d'un nouvel "
|
||||
"enregistrement concernant le modèle défini dans cette règle."
|
||||
|
||||
#. module: auditlog
|
||||
#: help:auditlog.rule,log_unlink:0
|
||||
msgid "Select this if you want to keep track of deletion on any record of the model of this rule"
|
||||
msgstr ""
|
||||
"Cochez cette case si vous voulez garder une trace des suppressions des "
|
||||
"enregistrements du modèle défini dans cette règle."
|
||||
|
||||
#. module: auditlog
|
||||
#: help:auditlog.rule,log_write:0
|
||||
msgid "Select this if you want to keep track of modification on any record of the model of this rule"
|
||||
msgstr ""
|
||||
"Cochez cette case si vous voulez garder une trace des modifications sur "
|
||||
"chaque enregistrement du modèle défini dans cette règle."
|
||||
|
||||
#. module: auditlog
|
||||
#: help:auditlog.rule,log_read:0
|
||||
msgid "Select this if you want to keep track of read/open on any record of the model of this rule"
|
||||
msgstr ""
|
||||
"Cochez cette case si vous voulez garder une trace de la lecture/ouverture de "
|
||||
"chaque enregistrement du modèle défini dans cette règle."
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.rule,state:0
|
||||
#: view:auditlog.rule:auditlog.view_auditlog_rule_search
|
||||
msgid "State"
|
||||
msgstr "État"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.rule:auditlog.view_auditlog_rule_form
|
||||
msgid "Subscribe"
|
||||
msgstr "Abonner"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.rule:auditlog.view_auditlog_rule_search
|
||||
#: selection:auditlog.rule,state:0
|
||||
msgid "Subscribed"
|
||||
msgstr "Abonné"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.log.line,field_name:0
|
||||
msgid "Technical name"
|
||||
msgstr "Nom technique"
|
||||
|
||||
#. module: auditlog
|
||||
#: sql_constraint:auditlog.rule:0
|
||||
msgid "There is already a rule defined on this model\n"
|
||||
"You cannot define another: please edit the existing one."
|
||||
msgstr ""
|
||||
"Il existe déjà une règle définie sur ce modèle\n"
|
||||
"Vous ne pouvez pas en définir une nouvelle, vous devez modifier celle "
|
||||
"existante."
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.rule:auditlog.view_auditlog_rule_form
|
||||
msgid "Unsubscribe"
|
||||
msgstr "Désabonner"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_search
|
||||
#: field:auditlog.log,user_id:0
|
||||
msgid "User"
|
||||
msgstr "Utilisateur"
|
||||
|
||||
#. module: auditlog
|
||||
#: field:auditlog.rule,user_id:0
|
||||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
|
||||
#. module: auditlog
|
||||
#: view:auditlog.log:auditlog.view_auditlog_log_form
|
||||
msgid "Values"
|
||||
msgstr "Valeurs"
|
||||
|
||||
#. module: auditlog
|
||||
#: code:addons/auditlog/models/rule.py:0
|
||||
#, python-format
|
||||
msgid "View logs"
|
||||
msgstr "Consulter les journaux"
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2013 ABF OSIELL (<http://osiell.com>).
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from . import rule
|
||||
from . import log
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,61 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2013 ABF OSIELL (<http://osiell.com>).
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import time
|
||||
|
||||
from openerp import models, fields
|
||||
|
||||
|
||||
class auditlog_log(models.Model):
|
||||
_name = 'auditlog.log'
|
||||
_description = "Auditlog - Log"
|
||||
_order = "timestamp desc"
|
||||
|
||||
name = fields.Char("Resource Name", size=64)
|
||||
model_id = fields.Many2one(
|
||||
'ir.model', string=u"Model")
|
||||
res_id = fields.Integer(u"Resource ID")
|
||||
user_id = fields.Many2one(
|
||||
'res.users', string=u"User")
|
||||
method = fields.Char(u"Method", size=64)
|
||||
timestamp = fields.Datetime(
|
||||
u"Date", default=lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'))
|
||||
line_ids = fields.One2many(
|
||||
'auditlog.log.line', 'log_id', string=u"Fields updated")
|
||||
|
||||
|
||||
class auditlog_log_line(models.Model):
|
||||
_name = 'auditlog.log.line'
|
||||
_description = "Auditlog - Log details (fields updated)"
|
||||
|
||||
field_id = fields.Many2one(
|
||||
'ir.model.fields', string=u"Field", required=True)
|
||||
log_id = fields.Many2one(
|
||||
'auditlog.log', string=u"Log", ondelete='cascade')
|
||||
#log = fields.Integer(u"Log ID")
|
||||
old_value = fields.Text(u"Old Value")
|
||||
new_value = fields.Text(u"New Value")
|
||||
old_value_text = fields.Text(u"Old value Text")
|
||||
new_value_text = fields.Text(u"New value Text")
|
||||
field_name = fields.Char(u"Technical name", size=64)
|
||||
field_description = fields.Char(u"Description", size=64)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,394 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2013 ABF OSIELL (<http://osiell.com>).
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import models, fields, api, modules
|
||||
from openerp.tools.translate import _
|
||||
|
||||
FIELDS_BLACKLIST = [
|
||||
'id', 'create_uid', 'create_date', 'write_uid', 'write_date',
|
||||
'display_name', '__last_update',
|
||||
]
|
||||
# Used for performance, to avoid a dictionary instanciation when we need an
|
||||
# empty dict to simplify algorithms
|
||||
EMPTY_DICT = {}
|
||||
|
||||
|
||||
class DictDiffer(object):
|
||||
"""Calculate the difference between two dictionaries as:
|
||||
(1) items added
|
||||
(2) items removed
|
||||
(3) keys same in both but changed values
|
||||
(4) keys same in both and unchanged values
|
||||
"""
|
||||
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.intersect = self.set_current.intersection(self.set_past)
|
||||
|
||||
def added(self):
|
||||
return self.set_current - self.intersect
|
||||
|
||||
def removed(self):
|
||||
return self.set_past - self.intersect
|
||||
|
||||
def changed(self):
|
||||
return set(o for o in self.intersect
|
||||
if self.past_dict[o] != self.current_dict[o])
|
||||
|
||||
def unchanged(self):
|
||||
return set(o for o in self.intersect
|
||||
if self.past_dict[o] == self.current_dict[o])
|
||||
|
||||
|
||||
class auditlog_rule(models.Model):
|
||||
_name = 'auditlog.rule'
|
||||
_description = "Auditlog - Rule"
|
||||
|
||||
name = fields.Char(u"Name", size=32, required=True)
|
||||
model_id = fields.Many2one(
|
||||
'ir.model', u"Model", required=True,
|
||||
help=u"Select model for which you want to generate log.")
|
||||
user_ids = fields.Many2many(
|
||||
'res.users',
|
||||
'audittail_rules_users',
|
||||
'user_id', 'rule_id',
|
||||
string=u"Users",
|
||||
help=u"if User is not added then it will applicable for all users")
|
||||
log_read = fields.Boolean(
|
||||
u"Log Reads",
|
||||
help=(u"Select this if you want to keep track of read/open on any "
|
||||
u"record of the model of this rule"))
|
||||
log_write = fields.Boolean(
|
||||
u"Log Writes", default=True,
|
||||
help=(u"Select this if you want to keep track of modification on any "
|
||||
u"record of the model of this rule"))
|
||||
log_unlink = fields.Boolean(
|
||||
u"Log Deletes", default=True,
|
||||
help=(u"Select this if you want to keep track of deletion on any "
|
||||
u"record of the model of this rule"))
|
||||
log_create = fields.Boolean(
|
||||
u"Log Creates", default=True,
|
||||
help=(u"Select this if you want to keep track of creation on any "
|
||||
u"record of the model of this rule"))
|
||||
#log_action = fields.Boolean(
|
||||
# "Log Action",
|
||||
# help=("Select this if you want to keep track of actions on the "
|
||||
# "model of this rule"))
|
||||
#log_workflow = fields.Boolean(
|
||||
# "Log Workflow",
|
||||
# help=("Select this if you want to keep track of workflow on any "
|
||||
# "record of the model of this rule"))
|
||||
state = fields.Selection(
|
||||
[('draft', "Draft"), ('subscribed', "Subscribed")],
|
||||
string=u"State", required=True, default='draft')
|
||||
action_id = fields.Many2one(
|
||||
'ir.actions.act_window', string="Action")
|
||||
|
||||
_sql_constraints = [
|
||||
('model_uniq', 'unique(model_id)',
|
||||
("There is already a rule defined on this model\n"
|
||||
"You cannot define another: please edit the existing one."))
|
||||
]
|
||||
|
||||
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 ids is None:
|
||||
ids = self.search(cr, 1, [('state', '=', 'subscribed')])
|
||||
return self._patch_methods(cr, 1, ids)
|
||||
|
||||
@api.multi
|
||||
def _patch_methods(self):
|
||||
"""Patch ORM methods of models defined in rules to log their calls."""
|
||||
updated = False
|
||||
for rule in self:
|
||||
if rule.state != 'subscribed':
|
||||
continue
|
||||
model_model = self.env[rule.model_id.model]
|
||||
# CRUD
|
||||
# -> create
|
||||
check_attr = 'auditlog_ruled_create'
|
||||
if getattr(rule, 'log_create') \
|
||||
and not hasattr(model_model, check_attr):
|
||||
model_model._patch_method('create', self._make_create())
|
||||
setattr(model_model, check_attr, True)
|
||||
updated = True
|
||||
# -> read
|
||||
check_attr = 'auditlog_ruled_read'
|
||||
if getattr(rule, 'log_read') \
|
||||
and not hasattr(model_model, check_attr):
|
||||
model_model._patch_method('read', self._make_read())
|
||||
setattr(model_model, check_attr, True)
|
||||
updated = True
|
||||
# -> write
|
||||
check_attr = 'auditlog_ruled_write'
|
||||
if getattr(rule, 'log_write') \
|
||||
and not hasattr(model_model, check_attr):
|
||||
model_model._patch_method('write', self._make_write())
|
||||
setattr(model_model, check_attr, True)
|
||||
updated = True
|
||||
# -> unlink
|
||||
check_attr = 'auditlog_ruled_unlink'
|
||||
if getattr(rule, 'log_unlink') \
|
||||
and not hasattr(model_model, check_attr):
|
||||
model_model._patch_method('unlink', self._make_unlink())
|
||||
setattr(model_model, check_attr, True)
|
||||
updated = True
|
||||
return updated
|
||||
|
||||
@api.multi
|
||||
def _revert_methods(self):
|
||||
"""Restore original ORM methods of models defined in rules."""
|
||||
updated = False
|
||||
for rule in self:
|
||||
model_model = self.env[rule.model_id.model]
|
||||
for method in ['create', 'read', 'write', 'unlink']:
|
||||
if getattr(rule, 'log_%s' % method):
|
||||
model_model._revert_method(method)
|
||||
updated = True
|
||||
if updated:
|
||||
modules.registry.RegistryManager.signal_registry_change(
|
||||
self.env.cr.dbname)
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
"""Update the registry when a new rule is created."""
|
||||
res_id = super(auditlog_rule, self).create(
|
||||
cr, uid, vals, context=context)
|
||||
if self._register_hook(cr, [res_id]):
|
||||
modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res_id
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
"""Update the registry when existing rules are updated."""
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
super(auditlog_rule, self).write(cr, uid, ids, vals, context=context)
|
||||
if self._register_hook(cr, ids):
|
||||
modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return True
|
||||
|
||||
def _make_create(self):
|
||||
"""Instanciate a create method that log its calls."""
|
||||
@api.model
|
||||
def create(self, vals, **kwargs):
|
||||
rule_model = self.env['auditlog.rule']
|
||||
new_record = create.origin(self, vals, **kwargs)
|
||||
new_values = dict(
|
||||
(d['id'], d) for d in new_record.sudo().read(
|
||||
list(self._columns)))
|
||||
rule_model.sudo().create_logs(
|
||||
self.env.uid, self._name, new_record.ids,
|
||||
'create', None, new_values)
|
||||
return new_record
|
||||
return create
|
||||
|
||||
def _make_read(self):
|
||||
"""Instanciate a read method that log its calls."""
|
||||
# FIXME: read() seems a bit tricky, improve to handle old/new api
|
||||
|
||||
#@api.v7
|
||||
#def read(self, cr, user, ids, fields=None, context=None, load='_classic_read', **kwargs):
|
||||
# print "LOG READ", fields, load, kwargs
|
||||
# # avoid loops
|
||||
# if self.env.context.get('auditlog_method_intercepted'):
|
||||
# return read.origin(self, cr, user, ids, fields, context, load, **kwargs)
|
||||
# # call original method with a modified context
|
||||
# context = dict(self.env.context, auditlog_method_intercepted=True)
|
||||
# result = read.origin(
|
||||
# self.with_context(context),
|
||||
# cr, user, ids, fields, context, load, **kwargs)
|
||||
# print "RESULT", result
|
||||
# return result
|
||||
|
||||
|
||||
#@api.v8
|
||||
#def read(self, fields=None, load='_classic_read', **kwargs):
|
||||
# print "LOG READ", fields, load, kwargs
|
||||
# # avoid loops
|
||||
# if self.env.context.get('auditlog_method_intercepted'):
|
||||
# return read.origin(self, fields, load, **kwargs)
|
||||
# # call original method with a modified context
|
||||
# context = dict(self.env.context, auditlog_method_intercepted=True)
|
||||
# result = read.origin(
|
||||
# self.with_context(context), fields, load, **kwargs)
|
||||
# print "RESULT", result
|
||||
# return result
|
||||
|
||||
def read(self, *args, **kwargs):
|
||||
result = read.origin(self, *args, **kwargs)
|
||||
return result
|
||||
return read
|
||||
|
||||
def _make_write(self):
|
||||
"""Instanciate a write method that log its calls."""
|
||||
@api.multi
|
||||
def write(self, vals, **kwargs):
|
||||
rule_model = self.env['auditlog.rule']
|
||||
old_values = dict(
|
||||
(d['id'], d) for d in self.sudo().read(list(self._columns)))
|
||||
result = write.origin(self, vals, **kwargs)
|
||||
new_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,
|
||||
'write', old_values, new_values)
|
||||
return result
|
||||
return write
|
||||
|
||||
def _make_unlink(self):
|
||||
"""Instanciate an unlink method that log its calls."""
|
||||
@api.multi
|
||||
def unlink(self, **kwargs):
|
||||
rule_model = self.env['auditlog.rule']
|
||||
rule_model.sudo().create_logs(
|
||||
self.env.uid, self._name, self.ids, 'unlink')
|
||||
return unlink.origin(self, **kwargs)
|
||||
return unlink
|
||||
|
||||
def create_logs(self, uid, res_model, res_ids, method,
|
||||
old_values=None, new_values=None):
|
||||
"""Create logs. `old_values` and `new_values` are dictionnaries, e.g:
|
||||
{RES_ID: {'FIELD': VALUE, ...}}
|
||||
"""
|
||||
if old_values is None:
|
||||
old_values = EMPTY_DICT
|
||||
if new_values is None:
|
||||
new_values = EMPTY_DICT
|
||||
log_model = self.env['auditlog.log']
|
||||
log_line_model = self.env['auditlog.log.line']
|
||||
ir_model = self.env['ir.model']
|
||||
ir_model_field = self.env['ir.model.fields']
|
||||
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,
|
||||
'res_id': res_id,
|
||||
'method': method,
|
||||
'user_id': uid,
|
||||
}
|
||||
log = log_model.create(vals)
|
||||
diff = DictDiffer(
|
||||
new_values.get(res_id, EMPTY_DICT),
|
||||
old_values.get(res_id, EMPTY_DICT))
|
||||
# 'write' case (old_values and new_values defined)
|
||||
for fchanged in diff.changed():
|
||||
if fchanged in FIELDS_BLACKLIST:
|
||||
continue
|
||||
field_ = ir_model_field.search(
|
||||
[('model_id', '=', model.id), ('name', '=', fchanged)])
|
||||
log_vals = {
|
||||
'field_id': field_.id,
|
||||
'field_name': field_.name,
|
||||
'field_description': field_.field_description,
|
||||
'log_id': log.id,
|
||||
'old_value': old_values[res_id][fchanged],
|
||||
'old_value_text': old_values[res_id][fchanged],
|
||||
'new_value': new_values[res_id][fchanged],
|
||||
'new_value_text': new_values[res_id][fchanged],
|
||||
}
|
||||
# for *2many fields, log the name_get
|
||||
if field_.relation and '2many' in field_.ttype:
|
||||
old_value_text = self.env[field_.relation].browse(
|
||||
log_vals['old_value']).name_get()
|
||||
log_vals['old_value_text'] = old_value_text
|
||||
new_value_text = self.env[field_.relation].browse(
|
||||
log_vals['new_value']).name_get()
|
||||
log_vals['new_value_text'] = new_value_text
|
||||
log_line_model.create(log_vals)
|
||||
# 'create' case (old_values => EMPTY_DICT)
|
||||
for fchanged in diff.added():
|
||||
if fchanged in FIELDS_BLACKLIST:
|
||||
continue
|
||||
field_ = ir_model_field.search(
|
||||
[('model_id', '=', model.id), ('name', '=', fchanged)])
|
||||
log_vals = {
|
||||
'field_id': field_.id,
|
||||
'field_name': field_.name,
|
||||
'field_description': field_.field_description,
|
||||
'log_id': log.id,
|
||||
'old_value': False,
|
||||
'old_value_text': False,
|
||||
'new_value': new_values[res_id][fchanged],
|
||||
'new_value_text': new_values[res_id][fchanged],
|
||||
}
|
||||
if field_.relation and '2many' in field_.ttype:
|
||||
new_value_text = self.env[field_.relation].browse(
|
||||
log_vals['new_value']).name_get()
|
||||
log_vals['new_value_text'] = new_value_text
|
||||
log_line_model.create(log_vals)
|
||||
|
||||
@api.multi
|
||||
def subscribe(self):
|
||||
"""Subscribe Rule for auditing changes on model and apply shortcut
|
||||
to view logs on that model.
|
||||
"""
|
||||
act_window_model = self.env['ir.actions.act_window']
|
||||
model_data_model = self.env['ir.model.data']
|
||||
for rule in self:
|
||||
# Create a shortcut to view logs
|
||||
domain = "[('model_id', '=', %s), ('res_id', '=', active_id)]" % (
|
||||
rule.model_id.id)
|
||||
vals = {
|
||||
'name': _(u"View logs"),
|
||||
'res_model': 'auditlog.log',
|
||||
'src_model': rule.model_id.model,
|
||||
'domain': domain,
|
||||
}
|
||||
act_window = act_window_model.sudo().create(vals)
|
||||
rule.write({'state': 'subscribed', 'action_id': act_window.id})
|
||||
keyword = 'client_action_relate'
|
||||
value = 'ir.actions.act_window,%s' % act_window.id
|
||||
model_data_model.sudo().ir_set(
|
||||
'action', keyword, 'View_log_' + rule.model_id.model,
|
||||
[rule.model_id.model], value, replace=True,
|
||||
isobject=True, xml_id=False)
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def unsubscribe(self):
|
||||
"""Unsubscribe Auditing Rule on model."""
|
||||
act_window_model = self.env['ir.actions.act_window']
|
||||
ir_values_model = self.env['ir.values']
|
||||
value = ''
|
||||
self._revert_methods()
|
||||
for rule in self:
|
||||
# Revert patched methods
|
||||
# Remove the shortcut to view logs
|
||||
act_window = act_window_model.search(
|
||||
[('name', '=', 'View Log'),
|
||||
('res_model', '=', 'auditlog.log'),
|
||||
('src_model', '=', rule.model_id.model)])
|
||||
if act_window:
|
||||
value = 'ir.actions.act_window,%s' % act_window.id
|
||||
act_window.unlink()
|
||||
if value:
|
||||
ir_value = ir_values_model.search(
|
||||
[('model', '=', rule.model_id.model), ('value', '=', value)])
|
||||
if ir_value:
|
||||
ir_value.unlink()
|
||||
self.write({'state': 'draft'})
|
||||
return True
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,8 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_auditlog_rule_user,auditlog_rule_user,model_auditlog_rule,base.group_user,0,0,0,0
|
||||
access_auditlog_log_user,auditlog_log_user,model_auditlog_log,base.group_user,0,0,0,0
|
||||
access_auditlog_log_line_user,auditlog_log_line_user,model_auditlog_log_line,base.group_user,0,0,0,0
|
||||
|
||||
access_auditlog_rule_manager,auditlog_rule_manager,model_auditlog_rule,base.group_erp_manager,1,1,1,1
|
||||
access_auditlog_log_manager,auditlog_log_manager,model_auditlog_log,base.group_erp_manager,1,1,1,1
|
||||
access_auditlog_log_line_manager,auditlog_log_line_manager,model_auditlog_log_line,base.group_erp_manager,1,1,1,1
|
|
|
@ -0,0 +1,191 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<menuitem id="menu_audit" name="Audit"
|
||||
parent="base.menu_reporting" sequence="50"
|
||||
groups="base.group_system"/>
|
||||
|
||||
|
||||
<!-- auditlog.rule -->
|
||||
|
||||
<record model="ir.ui.view" id="view_auditlog_rule_form">
|
||||
<field name="name">auditlog.rule.form</field>
|
||||
<field name="model">auditlog.rule</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Rule">
|
||||
<header>
|
||||
<button string="Subscribe" name="subscribe"
|
||||
type="object" states="draft" class="oe_highlight"/>
|
||||
<button string="Unsubscribe" name="unsubscribe"
|
||||
type="object" states="subscribed"/>
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group string="Rule">
|
||||
<group colspan="1">
|
||||
<field name="name" required="1"/>
|
||||
<field name="model_id"/>
|
||||
<field name="action_id" readonly="1" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group colspan="1">
|
||||
<field name="log_read" invisible="1"/>
|
||||
<field name="log_write"/>
|
||||
<field name="log_unlink"/>
|
||||
<field name="log_create"/>
|
||||
<!--<field name="log_action"/>-->
|
||||
<!--<field name="log_workflow"/>-->
|
||||
</group>
|
||||
</group>
|
||||
<!--
|
||||
<group string="Users">
|
||||
<field name="user_ids" nolabel="1"/>
|
||||
</group>
|
||||
-->
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_auditlog_rule_tree">
|
||||
<field name="name">auditlog.rule.tree</field>
|
||||
<field name="model">auditlog.rule</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree colors="blue:state == 'draft';black:state == 'subscribed'" string="Rules">
|
||||
<field name="name"/>
|
||||
<field name="model_id"/>
|
||||
<field name="log_read"/>
|
||||
<field name="log_write"/>
|
||||
<field name="log_unlink"/>
|
||||
<field name="log_create"/>
|
||||
<!--<field name="log_action"/>-->
|
||||
<!--<field name="log_workflow"/>-->
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_auditlog_rule_search" model="ir.ui.view">
|
||||
<field name="name">auditlog.rule.search</field>
|
||||
<field name="model">auditlog.rule</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Rules">
|
||||
<field name="name"/>
|
||||
<filter domain="[('state','=','draft')]" string="Draft"/>
|
||||
<filter domain="[('state','=','subscribed')]" string="Subscribed"/>
|
||||
<field name="model_id"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="State"
|
||||
domain="[]" context="{'group_by':'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_auditlog_rule_tree">
|
||||
<field name="name">Rules</field>
|
||||
<field name="res_model">auditlog.rule</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'search_default_draft': 1}</field>
|
||||
<field name="search_view_id" ref="view_auditlog_rule_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_action_auditlog_rule_tree" parent="menu_audit" action="action_auditlog_rule_tree"/>
|
||||
|
||||
|
||||
<!-- auditlog.log -->
|
||||
|
||||
<record model="ir.ui.view" id="view_auditlog_log_form">
|
||||
<field name="name">auditlog.log.form</field>
|
||||
<field name="model">auditlog.log</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Log">
|
||||
<sheet>
|
||||
<group string="Log">
|
||||
<group colspan="1">
|
||||
<field name="timestamp" required="1" readonly="1"/>
|
||||
<field name="user_id" readonly="1"/>
|
||||
<field name="method" readonly="1"/>
|
||||
</group>
|
||||
<group colspan="1">
|
||||
<field name="model_id" readonly="1"/>
|
||||
<field name="res_id" readonly="1"/>
|
||||
<field name="name" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Fields updated">
|
||||
<field name="line_ids" readonly="1" nolabel="1">
|
||||
<form string="Log - Field updated">
|
||||
<group>
|
||||
<field name="field_id" readonly="1"/>
|
||||
</group>
|
||||
<group string="Values" col="4">
|
||||
<field name="old_value" readonly="1"/>
|
||||
<field name="new_value" readonly="1"/>
|
||||
<field name="old_value_text" readonly="1"/>
|
||||
<field name="new_value_text" readonly="1"/>
|
||||
</group>
|
||||
</form>
|
||||
<tree>
|
||||
<field name="field_description"/>
|
||||
<field name="field_name"/>
|
||||
<!--<field name="old_value"/>-->
|
||||
<field name="old_value_text"/>
|
||||
<!--<field name="new_value"/>-->
|
||||
<field name="new_value_text"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_auditlog_log_tree">
|
||||
<field name="name">auditlog.log.tree</field>
|
||||
<field name="model">auditlog.log</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Logs" create="false">
|
||||
<field name="timestamp"/>
|
||||
<field name="name"/>
|
||||
<field name="model_id"/>
|
||||
<field name="res_id"/>
|
||||
<field name="method"/>
|
||||
<field name="user_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_auditlog_log_search" model="ir.ui.view">
|
||||
<field name="name">auditlog.log.search</field>
|
||||
<field name="model">auditlog.log</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Logs">
|
||||
<field name="name"/>
|
||||
<field name="model_id"/>
|
||||
<field name="res_id"/>
|
||||
<field name="user_id"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="User" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Model" domain="[]" context="{'group_by':'model_id'}"/>
|
||||
<filter string="Resource ID" domain="[]" context="{'group_by':'res_id'}"/>
|
||||
<filter string="Date" domain="[]" context="{'group_by':'timestamp'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_auditlog_log_tree">
|
||||
<field name="name">Logs</field>
|
||||
<field name="res_model">auditlog.log</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="search_view_id" ref="view_auditlog_log_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_audit_logs" name="Logs"
|
||||
parent="menu_audit" action="action_auditlog_log_tree"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
Loading…
Reference in New Issue