mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-22 03:13:12 +00:00
Merge branch 'complete_detailed_history' into 'dev'
Finish replacing old history view See merge request re2o/re2o!522
This commit is contained in:
commit
29b36bb346
10 changed files with 163 additions and 248 deletions
|
@ -92,6 +92,10 @@ class ActionsSearch:
|
|||
return classes
|
||||
|
||||
|
||||
############################
|
||||
# Machine history search #
|
||||
############################
|
||||
|
||||
class MachineHistorySearchEvent:
|
||||
def __init__(self, user, machine, interface, start=None, end=None):
|
||||
"""
|
||||
|
@ -280,16 +284,25 @@ class MachineHistorySearch:
|
|||
return self.events
|
||||
|
||||
|
||||
############################
|
||||
# Generic history classes #
|
||||
############################
|
||||
|
||||
class RelatedHistory:
|
||||
def __init__(self, name, model_name, object_id):
|
||||
def __init__(self, version):
|
||||
"""
|
||||
: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.model_name = model_name
|
||||
self.object_id = object_id
|
||||
self.version = version
|
||||
self.app_name = version.content_type.app_label
|
||||
self.model_name = version.content_type.model
|
||||
self.object_id = version.object_id
|
||||
self.name = version.object_repr
|
||||
|
||||
if self.model_name:
|
||||
self.name = "{}: {}".format(self.model_name.title(), self.name)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
|
@ -380,6 +393,7 @@ class History:
|
|||
if self._last_version is None:
|
||||
return None
|
||||
|
||||
self.name = self._last_version.object_repr
|
||||
return self.events[::-1]
|
||||
|
||||
def _compute_diff(self, v1, v2, ignoring=[]):
|
||||
|
@ -417,6 +431,10 @@ class History:
|
|||
self._last_version = version
|
||||
|
||||
|
||||
############################
|
||||
# Revision history #
|
||||
############################
|
||||
|
||||
class VersionAction(HistoryEvent):
|
||||
def __init__(self, version):
|
||||
self.version = version
|
||||
|
@ -496,6 +514,10 @@ class RevisionAction:
|
|||
return self.revision.get_comment()
|
||||
|
||||
|
||||
############################
|
||||
# Class-specific history #
|
||||
############################
|
||||
|
||||
class UserHistoryEvent(HistoryEvent):
|
||||
def _repr(self, name, value):
|
||||
"""
|
||||
|
@ -588,7 +610,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
|
||||
|
@ -624,17 +646,14 @@ class UserHistory(History):
|
|||
if obj is None:
|
||||
return None
|
||||
|
||||
# Add in "related" elements the list of Machine objects
|
||||
# Add in "related" elements the list of objects
|
||||
# that were once owned by this user
|
||||
self.related = (
|
||||
Version.objects.get_for_model(Machine)
|
||||
Version.objects.all()
|
||||
.filter(serialized_data__contains='"user": {}'.format(user_id))
|
||||
.order_by("-revision__date_created")
|
||||
.order_by("content_type__model")
|
||||
)
|
||||
self.related = [RelatedHistory(
|
||||
m.field_dict["name"] or _("None"),
|
||||
"machine",
|
||||
m.field_dict["id"]) for m in self.related]
|
||||
self.related = [RelatedHistory(v) for v in self.related]
|
||||
self.related = list(dict.fromkeys(self.related))
|
||||
|
||||
# Get all the versions for this user, with the oldest first
|
||||
|
@ -716,28 +735,18 @@ class MachineHistory(History):
|
|||
super(MachineHistory, self).__init__()
|
||||
self.event_type = MachineHistoryEvent
|
||||
|
||||
def get(self, machine_id):
|
||||
# Add as "related" histories the list of Interface objects
|
||||
# that were once assigned to this machine
|
||||
self.related = list(
|
||||
def get(self, machine_id, model):
|
||||
self.related = (
|
||||
Version.objects.get_for_model(Interface)
|
||||
.filter(serialized_data__contains='"machine": {}'.format(machine_id))
|
||||
.order_by("-revision__date_created")
|
||||
.order_by("content_type__model")
|
||||
)
|
||||
|
||||
# Create RelatedHistory objects and remove duplicates
|
||||
self.related = [RelatedHistory(
|
||||
i.field_dict["mac_address"] or _("None"),
|
||||
"interface",
|
||||
i.field_dict["id"]) for i in self.related]
|
||||
self.related = [RelatedHistory(v) for v in self.related]
|
||||
self.related = list(dict.fromkeys(self.related))
|
||||
|
||||
events = super(MachineHistory, self).get(machine_id, Machine)
|
||||
|
||||
# Update name
|
||||
self.name = self._last_version.field_dict["name"]
|
||||
|
||||
return events
|
||||
return super(MachineHistory, self).get(machine_id, Machine)
|
||||
|
||||
|
||||
class InterfaceHistoryEvent(HistoryEvent):
|
||||
|
@ -782,10 +791,29 @@ class InterfaceHistory(History):
|
|||
super(InterfaceHistory, self).__init__()
|
||||
self.event_type = InterfaceHistoryEvent
|
||||
|
||||
def get(self, interface_id):
|
||||
events = super(InterfaceHistory, self).get(interface_id, Interface)
|
||||
def get(self, interface_id, model):
|
||||
return 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"]()
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
{% extends 'logs/sidebar.html' %}
|
||||
{% comment %}
|
||||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||
se veut agnostique au réseau considéré, de manière à être installable en
|
||||
quelques clics.
|
||||
|
||||
Copyright © 2020 Jean-Romain Garnier
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load i18n %}
|
||||
{% load logs_extra %}
|
||||
|
||||
{% block title %}{% trans "History" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% blocktrans %}History of {{ title }}{% endblocktrans %}</h2>
|
||||
|
||||
{% if events %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Performed by" %}</th>
|
||||
<th>{% trans "Edited" %}</th>
|
||||
<th>{% trans "Comment" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for event in events %}
|
||||
<tr>
|
||||
<td>{{ event.date }}</td>
|
||||
<td>
|
||||
{% if event.performed_by %}
|
||||
<a href="{% url 'users:profil' userid=event.performed_by.id %}" title=tr_view_the_profile>
|
||||
{{ event.performed_by }}
|
||||
</a>
|
||||
{% else %}
|
||||
{% trans "Unknown" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% for edit in event.edits %}
|
||||
{% if edit.1 is None and edit.2 is None %}
|
||||
<strong>{{ edit.0 }}</strong><br/>
|
||||
{% elif edit.1 is None %}
|
||||
<strong>{{ edit.0 }}:</strong>
|
||||
<i class="text-success"> {{ edit.2 }}</i><br/>
|
||||
{% else %}
|
||||
<strong>{{ edit.0 }}:</strong>
|
||||
<i class="text-danger"> {{ edit.1 }} </i>
|
||||
➔ <i class="text-success">{{ edit.2 }}</i><br/>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>{{ event.comment }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% include 'pagination.html' with list=events %}
|
||||
{% else %}
|
||||
<h3>{% trans "No event" %}</h3>
|
||||
{% endif %}
|
||||
|
||||
{% if related_history %}
|
||||
|
||||
<h2>{% blocktrans %}Related elements{% endblocktrans %}</h2>
|
||||
|
||||
<ul>
|
||||
{% for related in related_history %}
|
||||
<li>
|
||||
<a title="{% trans "History" %}" href="{% url 'logs:detailed-history' related.model_name related.object_id %}">{{ related.model_name }} - {{ related.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
{% endblock %}
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -42,11 +42,6 @@ urlpatterns = [
|
|||
views.history,
|
||||
name="history",
|
||||
),
|
||||
url(
|
||||
r"(?P<object_name>\w+)/(?P<object_id>[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"),
|
||||
|
|
106
logs/views.py
106
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)
|
||||
return render(
|
||||
request, "re2o/history.html", {"reversions": reversions, "object": instance}
|
||||
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",
|
||||
{"title": title, "events": events, "related_history": history.related},
|
||||
)
|
||||
|
|
|
@ -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 %}
|
||||
</ul>
|
||||
</div>
|
||||
{% 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 %}
|
||||
|
|
|
@ -6,6 +6,7 @@ quelques clics.
|
|||
Copyright © 2017 Gabriel Détraz
|
||||
Copyright © 2017 Lara Kermarec
|
||||
Copyright © 2017 Augustin Lemesle
|
||||
Copyright © 2020 Jean-Romain Garnier
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -23,36 +24,73 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endcomment %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load logs_extra %}
|
||||
|
||||
{% if reversions.paginator %}
|
||||
<ul class="pagination nav navbar-nav">
|
||||
{% if reversions.has_previous %}
|
||||
<li><a href="?page={{ reversions.previous_page_number }}">{% trans "Next" %}</a></li>
|
||||
{% endif %}
|
||||
{% for page in reversions.paginator.page_range %}
|
||||
<li class="{% if reversions.number == page %}active{% endif %}"><a href="?page={{page }}">{{ page }}</a></li>
|
||||
{% endfor %}
|
||||
|
||||
{% if reversions.has_next %}
|
||||
<li> <a href="?page={{ reversions.next_page_number }}">{% trans "Previous" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<table class="table table-striped">
|
||||
{% if events %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Performed by" %}</th>
|
||||
<th>{% trans "Edited" %}</th>
|
||||
<th>{% trans "Comment" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for rev in reversions %}
|
||||
{% for event in events %}
|
||||
<tr>
|
||||
<td>{{ rev.revision.date_created }}</td>
|
||||
<td>{{ rev.revision.user }}</td>
|
||||
<td>{{ rev.revision.comment }}</td>
|
||||
<td>{{ event.date }}</td>
|
||||
<td>
|
||||
{% if event.performed_by %}
|
||||
<a href="{% url 'users:profil' userid=event.performed_by.id %}" title=tr_view_the_profile>
|
||||
{{ event.performed_by }}
|
||||
</a>
|
||||
{% else %}
|
||||
{% trans "Unknown" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% for edit in event.edits %}
|
||||
{% if edit.1 is None and edit.2 is None %}
|
||||
<strong>{{ edit.0 }}</strong><br/>
|
||||
{% elif edit.1 is None %}
|
||||
<strong>{{ edit.0 }}:</strong>
|
||||
<i class="text-success"> {{ edit.2 }}</i><br/>
|
||||
{% else %}
|
||||
<strong>{{ edit.0 }}:</strong>
|
||||
<i class="text-danger"> {{ edit.1 }} </i>
|
||||
➔ <i class="text-success">{{ edit.2 }}</i><br/>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>{{ event.comment }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</table>
|
||||
{% include 'pagination.html' with list=events %}
|
||||
{% else %}
|
||||
<h3>{% trans "No event" %}</h3>
|
||||
{% endif %}
|
||||
|
||||
{% if related_history %}
|
||||
|
||||
<h2>{% blocktrans %}Related elements{% endblocktrans %}</h2>
|
||||
|
||||
<ul>
|
||||
{% for related in related_history %}
|
||||
<li>
|
||||
{% if related.object_id %}
|
||||
<a href="{% url 'logs:history' related.app_name related.model_name related.object_id %}" title="{% trans "History" %}">
|
||||
{{ related.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ related.name }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ quelques clics.
|
|||
Copyright © 2017 Gabriel Détraz
|
||||
Copyright © 2017 Lara Kermarec
|
||||
Copyright © 2017 Augustin Lemesle
|
||||
Copyright © 2020 Jean-Romain Garnier
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -29,8 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% block title %}{% trans "History" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% blocktrans %}History of {{ object }}{% endblocktrans %}</h2>
|
||||
{% include 're2o/aff_history.html' with reversions=reversions %}
|
||||
<h2>{% blocktrans %}History of {{ title }}{% endblocktrans %}</h2>
|
||||
{% include 're2o/aff_history.html' with events=events related_history=related_history %}
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -24,13 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
{% load i18n %}
|
||||
|
||||
{% if detailed %}
|
||||
<a {% if class%}class="btn btn-info btn-sm"{% endif %} role="button" title="{% trans "History" %}" href="{% url 'logs:detailed-history' name id %}">
|
||||
<i class="fa fa-history"></i> {% if text %}{% trans "History" %}{% endif %}
|
||||
</a>
|
||||
{% else %}
|
||||
<a {% if class%}class="btn btn-info btn-sm"{% endif %} role="button" title="{% trans "History" %}" href="{% url 'logs:history' application name id %}">
|
||||
<i class="fa fa-history"></i> {% if text %}{% trans "History" %}{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% trans "Edit the groups" %}
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% history_button users text=True detailed=True %}
|
||||
{% history_button users text=True %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
|
Loading…
Reference in a new issue