Browse Source

Merge pull request #119 from osiell/8.0

[ADD] Module 'auditlog' - A substitute to the deprecated 'audittrail' module for Odoo 8
pull/93/merge
Stefan Rijnhart (Therp) 10 years ago
parent
commit
cdcfb0ebb3
  1. 49
      auditlog/README.rst
  2. 44
      auditlog/__init__.py
  3. 38
      auditlog/__openerp__.py
  4. 279
      auditlog/i18n/auditlog.pot
  5. 295
      auditlog/i18n/fr.po
  6. 26
      auditlog/migrations/8.0.1.0/pre-migration.py
  7. 23
      auditlog/models/__init__.py
  8. 55
      auditlog/models/log.py
  9. 451
      auditlog/models/rule.py
  10. 8
      auditlog/security/ir.model.access.csv
  11. 21
      auditlog/tests/__init__.py
  12. 77
      auditlog/tests/test_auditlog.py
  13. 201
      auditlog/views/auditlog_view.xml

49
auditlog/README.rst

@ -0,0 +1,49 @@
Track user operation on data models
===================================
This module allows the administrator to log user operations performed on data
models such as ``create``, ``read``, ``write`` and ``delete``.
Usage
=====
Go to `Reporting / Audit / Rules` to subscribe rules. A rule defines which
operations to log for a given data model.
Then, check logs in the `Reporting / Audit / Logs` menu.
During installation, it will migrate any existing data from the `audittrail`
module (rules and logs).
For further information, please visit:
* https://www.odoo.com/forum/help-1
Known issues / Roadmap
======================
* log ``read`` operations
* log only operations triggered by some users (currently it logs all users)
* group logs by HTTP query (thanks to werzeug)?
* group HTTP query by user session?
Credits
=======
Contributors
------------
* Sebastien Alix <sebastien.alix@osiell.com>
* Holger Brunn <hbrunn@therp.nl>
Maintainer
----------
.. image:: http://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: http://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.

44
auditlog/__init__.py

@ -0,0 +1,44 @@
# -*- 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
def pre_init_hook(cr):
cr.execute("SELECT 1 FROM pg_class WHERE relname = 'audittrail_rule'")
if cr.fetchall():
migrate_from_audittrail(cr)
def migrate_from_audittrail(cr):
cr.execute('ALTER TABLE audittrail_rule RENAME TO auditlog_rule')
cr.execute('ALTER TABLE audittrail_rule_id_seq '
'RENAME TO auditlog_rule_id_seq')
cr.execute('ALTER TABLE auditlog_rule RENAME COLUMN object_id TO model_id')
cr.execute('ALTER TABLE audittrail_log RENAME TO auditlog_log')
cr.execute('ALTER TABLE audittrail_log_id_seq '
'RENAME TO auditlog_log_id_seq')
cr.execute('ALTER TABLE auditlog_log RENAME COLUMN object_id TO model_id')
cr.execute('ALTER TABLE audittrail_log_line RENAME TO auditlog_log_line')
cr.execute('ALTER TABLE audittrail_log_line_id_seq '
'RENAME TO auditlog_log_line_id_seq')
cr.execute("UPDATE ir_model_data SET model='auditlog.rule' "
"WHERE model='audittrail.rule'")

38
auditlog/__openerp__.py

@ -0,0 +1,38 @@
# -*- 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",
'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,
'pre_init_hook': 'pre_init_hook',
}

279
auditlog/i18n/auditlog.pot

@ -0,0 +1,279 @@
# 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 13:33+0000\n"
"PO-Revision-Date: 2015-01-22 13:33+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 ""
#. module: auditlog
#: model:ir.ui.menu,name:auditlog.menu_audit
msgid "Audit"
msgstr ""
#. module: auditlog
#: model:ir.model,name:auditlog.model_auditlog_log
msgid "Auditlog - Log"
msgstr ""
#. module: auditlog
#: model:ir.model,name:auditlog.model_auditlog_log_line
msgid "Auditlog - Log details (fields updated)"
msgstr ""
#. module: auditlog
#: model:ir.model,name:auditlog.model_auditlog_rule
msgid "Auditlog - Rule"
msgstr ""
#. 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
#: field:auditlog.log.line,field_description:0
msgid "Description"
msgstr ""
#. module: auditlog
#: view:auditlog.rule:auditlog.view_auditlog_rule_search
#: selection:auditlog.rule,state:0
msgid "Draft"
msgstr ""
#. module: auditlog
#: field:auditlog.log.line,field_id:0
msgid "Field"
msgstr ""
#. module: auditlog
#: view:auditlog.log:auditlog.view_auditlog_log_form
#: field:auditlog.log,line_ids:0
msgid "Fields updated"
msgstr ""
#. module: auditlog
#: view:auditlog.log:auditlog.view_auditlog_log_search
#: view:auditlog.rule:auditlog.view_auditlog_rule_search
msgid "Group By..."
msgstr ""
#. module: auditlog
#: field:auditlog.log,id:0
#: field:auditlog.log.line,id:0
#: field:auditlog.rule,id:0
msgid "ID"
msgstr ""
#. 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 ""
#. module: auditlog
#: view:auditlog.log:auditlog.view_auditlog_log_form
msgid "Log - Field updated"
msgstr ""
#. module: auditlog
#: field:auditlog.rule,log_create:0
msgid "Log Creates"
msgstr ""
#. module: auditlog
#: field:auditlog.rule,log_unlink:0
msgid "Log Deletes"
msgstr ""
#. module: auditlog
#: field:auditlog.rule,log_read:0
msgid "Log Reads"
msgstr ""
#. module: auditlog
#: field:auditlog.rule,log_write:0
msgid "Log Writes"
msgstr ""
#. 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 ""
#. module: auditlog
#: field:auditlog.log,method:0
msgid "Method"
msgstr ""
#. 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 ""
#. module: auditlog
#: field:auditlog.rule,name:0
msgid "Name"
msgstr ""
#. module: auditlog
#: field:auditlog.log.line,new_value:0
msgid "New Value"
msgstr ""
#. module: auditlog
#: field:auditlog.log.line,new_value_text:0
msgid "New value Text"
msgstr ""
#. module: auditlog
#: field:auditlog.log.line,old_value:0
msgid "Old Value"
msgstr ""
#. module: auditlog
#: field:auditlog.log.line,old_value_text:0
msgid "Old value Text"
msgstr ""
#. module: auditlog
#: view:auditlog.log:auditlog.view_auditlog_log_search
#: field:auditlog.log,res_id:0
msgid "Resource ID"
msgstr ""
#. module: auditlog
#: field:auditlog.log,name:0
msgid "Resource Name"
msgstr ""
#. module: auditlog
#: view:auditlog.rule:auditlog.view_auditlog_rule_form
msgid "Rule"
msgstr ""
#. 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 ""
#. module: auditlog
#: help:auditlog.rule,model_id:0
msgid "Select model for which you want to generate log."
msgstr ""
#. 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 ""
#. 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 ""
#. 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 ""
#. 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 ""
#. module: auditlog
#: view:auditlog.rule:auditlog.view_auditlog_rule_search
#: field:auditlog.rule,state:0
msgid "State"
msgstr ""
#. module: auditlog
#: view:auditlog.rule:auditlog.view_auditlog_rule_form
msgid "Subscribe"
msgstr ""
#. module: auditlog
#: view:auditlog.rule:auditlog.view_auditlog_rule_search
#: selection:auditlog.rule,state:0
msgid "Subscribed"
msgstr ""
#. module: auditlog
#: field:auditlog.log.line,field_name:0
msgid "Technical name"
msgstr ""
#. 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 ""
#. module: auditlog
#: view:auditlog.rule:auditlog.view_auditlog_rule_form
msgid "Unsubscribe"
msgstr ""
#. module: auditlog
#: view:auditlog.log:auditlog.view_auditlog_log_search
#: field:auditlog.log,user_id:0
msgid "User"
msgstr ""
#. module: auditlog
#: field:auditlog.rule,user_ids:0
msgid "Users"
msgstr ""
#. module: auditlog
#: view:auditlog.log:auditlog.view_auditlog_log_form
msgid "Values"
msgstr ""

295
auditlog/i18n/fr.po

@ -0,0 +1,295 @@
# 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 "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"

26
auditlog/migrations/8.0.1.0/pre-migration.py

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2015 Therp BV (<http://therp.nl>).
#
# 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.addons.auditlog import migrate_from_audittrail
def migrate(cr, version):
"""if we migrate from an older version, it's a migration from audittrail"""
migrate_from_audittrail(cr)

23
auditlog/models/__init__.py

@ -0,0 +1,23 @@
# -*- 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

55
auditlog/models/log.py

@ -0,0 +1,55 @@
# -*- 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
class auditlog_log(models.Model):
_name = 'auditlog.log'
_description = "Auditlog - Log"
_order = "create_date 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)
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', ondelete='cascade', string=u"Field", required=True)
log_id = fields.Many2one(
'auditlog.log', string=u"Log", ondelete='cascade')
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", related='field_id.name')
field_description = fields.Char(
u"Description", related='field_id.field_description')

451
auditlog/models/rule.py

@ -0,0 +1,451 @@
# -*- 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, _, SUPERUSER_ID
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)
self.set_past = set(past_dict)
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 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, 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.id
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)
# Unable to find a way to declare the `create` method with the new API,
# errors occurs with the `_register_hook()` BaseModel method.
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
# Unable to find a way to declare the `write` method with the new API,
# errors occurs with the `_register_hook()` BaseModel method.
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']
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', 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,
additional_log_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']
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': self.pool._auditlog_model_cache[res_model],
'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),
old_values.get(res_id, EMPTY_DICT))
self._create_log_line_on_write(
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, {})
# We use 'search()' then 'read()' instead of the 'search_read()'
# to take advantage of the 'classic_write' loading
field_model = self.env['ir.model.fields']
field = field_model.search(
[('model_id', '=', model.id), ('name', '=', field_name)])
field_data = field.read(load='_classic_write')[0]
cache[model.model][field_name] = field_data
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']
for field_name in fields_list:
if field_name in FIELDS_BLACKLIST:
continue
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)
def _prepare_log_line_vals_on_write(
self, log, field, old_values, new_values):
"""Prepare the dictionary of values used to create a log line on a
'write' operation.
"""
vals = {
'field_id': field['id'],
'log_id': log.id,
'old_value': old_values[log.res_id][field['name']],
'old_value_text': old_values[log.res_id][field['name']],
'new_value': new_values[log.res_id][field['name']],
'new_value_text': new_values[log.res_id][field['name']],
}
# for *2many fields, log the name_get
if field['relation'] and '2many' in field['ttype']:
# Filter IDs to prevent a 'name_get()' call on deleted resources
existing_ids = self.env[field['relation']]._search(
[('id', 'in', vals['old_value'])])
old_value_text = []
if existing_ids:
existing_values = self.env[field['relation']].browse(
existing_ids).name_get()
old_value_text.extend(existing_values)
# Deleted resources will have a 'DELETED' text representation
deleted_ids = set(vals['old_value']) - set(existing_ids)
for deleted_id in deleted_ids:
old_value_text.append((deleted_id, 'DELETED'))
vals['old_value_text'] = old_value_text
new_value_text = self.env[field['relation']].browse(
vals['new_value']).name_get()
vals['new_value_text'] = new_value_text
return vals
def _create_log_line_on_create(
self, log, fields_list, new_values):
"""Log field filled on a 'create' operation."""
log_line_model = self.env['auditlog.log.line']
for field_name in fields_list:
if field_name in FIELDS_BLACKLIST:
continue
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)
def _prepare_log_line_vals_on_create(self, log, field, new_values):
"""Prepare the dictionary of values used to create a log line on a
'create' operation.
"""
vals = {
'field_id': field['id'],
'log_id': log.id,
'old_value': False,
'old_value_text': False,
'new_value': new_values[log.res_id][field['name']],
'new_value_text': new_values[log.res_id][field['name']],
}
if field['relation'] and '2many' in field['ttype']:
new_value_text = self.env[field['relation']].browse(
vals['new_value']).name_get()
vals['new_value_text'] = new_value_text
return 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']
# Revert patched methods
self._revert_methods()
for rule in self:
# 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()
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

8
auditlog/security/ir.model.access.csv

@ -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

21
auditlog/tests/__init__.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2015 Therp BV (<http://therp.nl>).
#
# 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 test_auditlog

77
auditlog/tests/test_auditlog.py

@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2015 Therp BV (<http://therp.nl>).
#
# 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.tests.common import TransactionCase
class TestAuditlog(TransactionCase):
def test_LogCreation(self):
"""First test, caching some data."""
auditlog_log = self.env['auditlog.log']
groups_model_id = self.env.ref('base.model_res_groups').id
self.env['auditlog.rule'].create({
'name': 'testrule for groups',
'model_id': groups_model_id,
'log_create': True,
'log_write': True,
'log_unlink': True,
'state': 'subscribed',
})
group = self.env['res.groups'].create({
'name': 'testgroup1',
})
self.assertTrue(auditlog_log.search([
('model_id', '=', groups_model_id),
('method', '=', 'create'),
('res_id', '=', group.id),
]))
group.write({'name': 'Testgroup1'})
self.assertTrue(auditlog_log.search([
('model_id', '=', groups_model_id),
('method', '=', 'write'),
('res_id', '=', group.id),
]))
group.unlink()
self.assertTrue(auditlog_log.search([
('model_id', '=', groups_model_id),
('method', '=', 'unlink'),
('res_id', '=', group.id),
]))
def test_LogCreation2(self):
"""Second test, using cached data of the first one."""
self.env['res.groups'].create({
'name': 'testgroup2',
})
def test_LogCreation3(self):
"""Third test, two groups, the latter being the parent of the former.
Then we remove it right after (with (2, X) tuple) to test the creation
of a 'write' log with a deleted resource (so with no text
representation).
"""
testgroup3 = self.env['res.groups'].create({
'name': 'testgroup3',
})
testgroup4 = self.env['res.groups'].create({
'name': 'testgroup4',
'implied_ids': [(4, testgroup3.id)],
})
testgroup4.write({'implied_ids': [(2, testgroup3.id)]})

201
auditlog/views/auditlog_view.xml

@ -0,0 +1,201 @@
<?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 name="state_draft"
domain="[('state','=','draft')]" string="Draft"/>
<filter name="state_subscribed"
domain="[('state','=','subscribed')]" string="Subscribed"/>
<field name="model_id"/>
<group expand="0" string="Group By...">
<filter name="group_by_state" 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">{}</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="create_date" 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="create_date"/>
<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 name="group_by_user_id"
string="User"
domain="[]" context="{'group_by':'user_id'}"/>
<filter name="group_by_model_id"
string="Model"
domain="[]" context="{'group_by':'model_id'}"/>
<filter name="group_by_res_id"
string="Resource ID"
domain="[]" context="{'group_by':'res_id'}"/>
<filter name="group_by_create_date"
string="Date"
domain="[]" context="{'group_by':'create_date'}"/>
</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…
Cancel
Save