diff --git a/logs/models.py b/logs/models.py
index acf8771b..112d536f 100644
--- a/logs/models.py
+++ b/logs/models.py
@@ -92,6 +92,10 @@ class ActionsSearch:
return classes
+############################
+# Machine history search #
+############################
+
class MachineHistorySearchEvent:
def __init__(self, user, machine, interface, start=None, end=None):
"""
@@ -280,14 +284,19 @@ class MachineHistorySearch:
return self.events
+############################
+# Generic history classes #
+############################
+
class RelatedHistory:
- def __init__(self, name, model_name, object_id):
+ def __init__(self, name, app_name, model_name, object_id):
"""
:param name: Name of this instance
:param model_name: Name of the related model (e.g. "user")
:param object_id: ID of the related object
"""
self.name = "{} (id = {})".format(name, object_id)
+ self.app_name = app_name
self.model_name = model_name
self.object_id = object_id
@@ -417,6 +426,10 @@ class History:
self._last_version = version
+############################
+# Revision history #
+############################
+
class VersionAction(HistoryEvent):
def __init__(self, version):
self.version = version
@@ -496,6 +509,10 @@ class RevisionAction:
return self.revision.get_comment()
+############################
+# Class-specific history #
+############################
+
class UserHistoryEvent(HistoryEvent):
def _repr(self, name, value):
"""
@@ -588,7 +605,7 @@ class UserHistory(History):
super(UserHistory, self).__init__()
self.event_type = UserHistoryEvent
- def get(self, user_id):
+ def get(self, user_id, model):
"""
:param user_id: int, the id of the user to lookup
:return: list or None, a list of UserHistoryEvent, in reverse chronological order
@@ -633,7 +650,8 @@ class UserHistory(History):
)
self.related = [RelatedHistory(
m.field_dict["name"] or _("None"),
- "machine",
+ "machines"
+ "Machine",
m.field_dict["id"]) for m in self.related]
self.related = list(dict.fromkeys(self.related))
@@ -716,7 +734,7 @@ class MachineHistory(History):
super(MachineHistory, self).__init__()
self.event_type = MachineHistoryEvent
- def get(self, machine_id):
+ def get(self, machine_id, model):
# Add as "related" histories the list of Interface objects
# that were once assigned to this machine
self.related = list(
@@ -728,7 +746,8 @@ class MachineHistory(History):
# Create RelatedHistory objects and remove duplicates
self.related = [RelatedHistory(
i.field_dict["mac_address"] or _("None"),
- "interface",
+ "machines",
+ "Interface",
i.field_dict["id"]) for i in self.related]
self.related = list(dict.fromkeys(self.related))
@@ -782,10 +801,34 @@ class InterfaceHistory(History):
super(InterfaceHistory, self).__init__()
self.event_type = InterfaceHistoryEvent
- def get(self, interface_id):
+ def get(self, interface_id, model):
events = super(InterfaceHistory, self).get(interface_id, Interface)
# Update name
self.name = self._last_version.field_dict["mac_address"]
return events
+
+
+############################
+# History auto-detect #
+############################
+
+HISTORY_CLASS_MAPPING = {
+ User: UserHistory,
+ Machine: MachineHistory,
+ Interface: InterfaceHistory,
+ "default": History
+}
+
+
+def get_history_class(model):
+ """
+ Find the mos appropriate History subclass to represent
+ the given model's history
+ :model: class
+ """
+ try:
+ return HISTORY_CLASS_MAPPING[model]()
+ except KeyError:
+ return HISTORY_CLASS_MAPPING["default"]()
diff --git a/logs/templates/logs/detailed_history.html b/logs/templates/logs/detailed_history.html
index 7aaf67a0..8ffe5d68 100644
--- a/logs/templates/logs/detailed_history.html
+++ b/logs/templates/logs/detailed_history.html
@@ -82,7 +82,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
diff --git a/logs/templatetags/logs_extra.py b/logs/templatetags/logs_extra.py
index 2e58cb67..c436c1fa 100644
--- a/logs/templatetags/logs_extra.py
+++ b/logs/templatetags/logs_extra.py
@@ -42,7 +42,7 @@ def is_facture(baseinvoice):
@register.inclusion_tag("buttons/history.html")
-def history_button(instance, text=False, detailed=False, html_class=True):
+def history_button(instance, text=False, html_class=True):
"""Creates the correct history button for an instance.
Args:
@@ -57,6 +57,5 @@ def history_button(instance, text=False, detailed=False, html_class=True):
"name": instance._meta.model_name,
"id": instance.id,
"text": text,
- "detailed": detailed,
"class": html_class,
}
diff --git a/logs/urls.py b/logs/urls.py
index 914761bf..d70cc4a8 100644
--- a/logs/urls.py
+++ b/logs/urls.py
@@ -42,11 +42,6 @@ urlpatterns = [
views.history,
name="history",
),
- url(
- r"(?P\w+)/(?P[0-9]+)$",
- views.detailed_history,
- name="detailed-history",
- ),
url(r"^stats_general/$", views.stats_general, name="stats-general"),
url(r"^stats_models/$", views.stats_models, name="stats-models"),
url(r"^stats_users/$", views.stats_users, name="stats-users"),
diff --git a/logs/views.py b/logs/views.py
index b71187aa..69102b31 100644
--- a/logs/views.py
+++ b/logs/views.py
@@ -37,7 +37,6 @@ nombre d'objets par models, nombre d'actions par user, etc
"""
from __future__ import unicode_literals
-from itertools import chain
from django.urls import reverse
from django.shortcuts import render, redirect
@@ -105,9 +104,7 @@ from .models import (
ActionsSearch,
RevisionAction,
MachineHistorySearch,
- UserHistory,
- MachineHistory,
- InterfaceHistory
+ get_history_class
)
from .forms import (
@@ -526,33 +523,24 @@ def stats_search_machine_history(request):
return render(request, "logs/search_machine_history.html", {"history_form": history_form})
-def get_history_object(request, model, object_name, object_id, allow_deleted=False):
+def get_history_object(request, model, object_name, object_id):
"""Get the objet of type model with the given object_id
Handles permissions and DoesNotExist errors
"""
- is_deleted = False
-
try:
object_name_id = object_name + "id"
kwargs = {object_name_id: object_id}
instance = model.get_instance(**kwargs)
except model.DoesNotExist:
- is_deleted = True
instance = None
- if is_deleted and not allow_deleted:
- messages.error(request, _("Nonexistent entry."))
- return False, redirect(
- reverse("users:profil", kwargs={"userid": str(request.user.id)})
- )
-
- if is_deleted:
- can_view = can_view_app("logs")
+ if instance is None:
+ authorized = can_view_app("logs")
msg = None
else:
- can_view, msg, _permissions = instance.can_view(request.user)
+ authorized, msg, _permissions = instance.can_view(request.user)
- if not can_view:
+ if not authorized:
messages.error(
request, msg or _("You don't have the right to access this menu.")
)
@@ -563,61 +551,14 @@ def get_history_object(request, model, object_name, object_id, allow_deleted=Fal
return True, instance
-@login_required
-def detailed_history(request, object_name, object_id):
- """Render a detailed history for a model.
- Permissions are handled by get_history_object.
- """
- # Only handle objects for which a detailed view exists
- if object_name == "user":
- model = User
- history = UserHistory()
- elif object_name == "machine":
- model = Machine
- history = MachineHistory()
- elif object_name == "interface":
- model = Interface
- history = InterfaceHistory()
- else:
- raise Http404(_("No model found."))
-
- # Get instance and check permissions
- can_view, instance = get_history_object(request, model, object_name, object_id, allow_deleted=True)
- if not can_view:
- return instance
-
- # Generate the pagination with the objects
- max_result = GeneralOption.get_cached_value("pagination_number")
- events = history.get(int(object_id))
-
- # Events is None if object wasn't found
- if events is None:
- messages.error(request, _("Nonexistent entry."))
- return redirect(
- reverse("users:profil", kwargs={"userid": str(request.user.id)})
- )
-
- # Add the paginator in case there are many results
- events = re2o_paginator(request, events, max_result)
-
- # Add a title in case the object was deleted
- title = instance or "{} ({})".format(history.name, _("Deleted"))
-
- return render(
- request,
- "logs/detailed_history.html",
- {"title": title, "events": events, "related_history": history.related},
- )
-
-
@login_required
def history(request, application, object_name, object_id):
"""Render history for a model.
The model is determined using the `HISTORY_BIND` dictionnary if none is
found, raises a Http404. The view checks if the user is allowed to see the
- history using the `can_view` method of the model.
- Permissions are handled by get_history_object.
+ history using the `can_view` method of the model, or the generic
+ `can_view_app("logs")` for deleted objects (see `get_history_object`).
Args:
request: The request sent by the user.
@@ -637,16 +578,29 @@ def history(request, application, object_name, object_id):
except LookupError:
raise Http404(_("No model found."))
- can_view, instance = get_history_object(request, model, object_name, object_id)
+ authorized, instance = get_history_object(request, model, object_name, object_id)
if not can_view:
return instance
- pagination_number = GeneralOption.get_cached_value("pagination_number")
- reversions = Version.objects.get_for_object(instance)
- if hasattr(instance, "linked_objects"):
- for related_object in chain(instance.linked_objects()):
- reversions = reversions | Version.objects.get_for_object(related_object)
- reversions = re2o_paginator(request, reversions, pagination_number)
+ history = get_history_class(model)
+ events = history.get(int(object_id), model)
+
+ # Events is None if object wasn't found
+ if events is None:
+ messages.error(request, _("Nonexistent entry."))
+ return redirect(
+ reverse("users:profil", kwargs={"userid": str(request.user.id)})
+ )
+
+ # Generate the pagination with the objects
+ max_result = GeneralOption.get_cached_value("pagination_number")
+ events = re2o_paginator(request, events, max_result)
+
+ # Add a default title in case the object was deleted
+ title = instance or "{} ({})".format(history.name, _("Deleted"))
+
return render(
- request, "re2o/history.html", {"reversions": reversions, "object": instance}
+ request,
+ "logs/detailed_history.html",
+ {"title": title, "events": events, "related_history": history.related},
)
diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html
index 857d2a3a..77b65546 100644
--- a/machines/templates/machines/aff_machines.html
+++ b/machines/templates/machines/aff_machines.html
@@ -67,7 +67,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% trans "Create an interface" as tr_create_an_interface %}
{% include 'buttons/add.html' with href='machines:new-interface' id=machine.id desc=tr_create_an_interface %}
{% acl_end %}
- {% history_button machine detailed=True %}
+ {% history_button machine %}
{% can_delete machine %}
{% include 'buttons/suppr.html' with href='machines:del-machine' id=machine.id %}
{% acl_end %}
@@ -161,7 +161,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% acl_end %}
- {% history_button interface detailed=True %}
+ {% history_button interface %}
{% can_delete interface %}
{% include 'buttons/suppr.html' with href='machines:del-interface' id=interface.id %}
{% acl_end %}
diff --git a/templates/buttons/history.html b/templates/buttons/history.html
index 2495dec8..71c2c71d 100644
--- a/templates/buttons/history.html
+++ b/templates/buttons/history.html
@@ -24,13 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load i18n %}
-{% if detailed %}
-
- {% if text %}{% trans "History" %}{% endif %}
-
-{% else %}
{% if text %}{% trans "History" %}{% endif %}
-{% endif %}
diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html
index 4fa780a7..4fec3c18 100644
--- a/users/templates/users/profil.html
+++ b/users/templates/users/profil.html
@@ -176,7 +176,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% trans "Edit the groups" %}
{% acl_end %}
- {% history_button users text=True detailed=True %}
+ {% history_button users text=True %}