mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
Closes #6150: Enable change logging for journal entries
This commit is contained in:
parent
e5bbf47ab9
commit
a296a9e109
@ -10,6 +10,7 @@
|
|||||||
* [#6121](https://github.com/netbox-community/netbox/issues/6121) - Extend parent interface assignment to VM interfaces
|
* [#6121](https://github.com/netbox-community/netbox/issues/6121) - Extend parent interface assignment to VM interfaces
|
||||||
* [#6125](https://github.com/netbox-community/netbox/issues/6125) - Add locations count to home page
|
* [#6125](https://github.com/netbox-community/netbox/issues/6125) - Add locations count to home page
|
||||||
* [#6146](https://github.com/netbox-community/netbox/issues/6146) - Add bulk disconnect support for power feeds
|
* [#6146](https://github.com/netbox-community/netbox/issues/6146) - Add bulk disconnect support for power feeds
|
||||||
|
* [#6150](https://github.com/netbox-community/netbox/issues/6150) - Enable change logging for journal entries
|
||||||
|
|
||||||
### Bug Fixes (from Beta)
|
### Bug Fixes (from Beta)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ class Migration(migrations.Migration):
|
|||||||
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
('assigned_object_id', models.PositiveIntegerField()),
|
('assigned_object_id', models.PositiveIntegerField()),
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||||
('kind', models.CharField(default='info', max_length=30)),
|
('kind', models.CharField(default='info', max_length=30)),
|
||||||
('comments', models.TextField()),
|
('comments', models.TextField()),
|
||||||
('assigned_object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
|
('assigned_object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
|
||||||
|
@ -7,13 +7,15 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.core.validators import ValidationError
|
from django.core.validators import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.formats import date_format, time_format
|
||||||
from rest_framework.utils.encoders import JSONEncoder
|
from rest_framework.utils.encoders import JSONEncoder
|
||||||
|
|
||||||
from extras.choices import *
|
from extras.choices import *
|
||||||
from extras.constants import *
|
from extras.constants import *
|
||||||
from extras.utils import extras_features, FeatureQuery, image_upload
|
from extras.utils import extras_features, FeatureQuery, image_upload
|
||||||
from netbox.models import BigIDModel
|
from netbox.models import BigIDModel, ChangeLoggedModel
|
||||||
from utilities.querysets import RestrictedQuerySet
|
from utilities.querysets import RestrictedQuerySet
|
||||||
from utilities.utils import render_jinja2
|
from utilities.utils import render_jinja2
|
||||||
|
|
||||||
@ -389,7 +391,7 @@ class ImageAttachment(BigIDModel):
|
|||||||
# Journal entries
|
# Journal entries
|
||||||
#
|
#
|
||||||
|
|
||||||
class JournalEntry(BigIDModel):
|
class JournalEntry(ChangeLoggedModel):
|
||||||
"""
|
"""
|
||||||
A historical remark concerning an object; collectively, these form an object's journal. The journal is used to
|
A historical remark concerning an object; collectively, these form an object's journal. The journal is used to
|
||||||
preserve historical context around an object, and complements NetBox's built-in change logging. For example, you
|
preserve historical context around an object, and complements NetBox's built-in change logging. For example, you
|
||||||
@ -427,8 +429,10 @@ class JournalEntry(BigIDModel):
|
|||||||
verbose_name_plural = 'journal entries'
|
verbose_name_plural = 'journal entries'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
time_created = self.created.replace(microsecond=0)
|
return f"{date_format(self.created)} - {time_format(self.created)} ({self.get_kind_display()})"
|
||||||
return f"{time_created} - {self.get_kind_display()}"
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('extras:journalentry', args=[self.pk])
|
||||||
|
|
||||||
def get_kind_class(self):
|
def get_kind_class(self):
|
||||||
return JournalEntryKindChoices.CSS_CLASSES.get(self.kind)
|
return JournalEntryKindChoices.CSS_CLASSES.get(self.kind)
|
||||||
|
@ -102,15 +102,15 @@ class ObjectJournalTable(BaseTable):
|
|||||||
Used for displaying a set of JournalEntries within the context of a single object.
|
Used for displaying a set of JournalEntries within the context of a single object.
|
||||||
"""
|
"""
|
||||||
created = tables.DateTimeColumn(
|
created = tables.DateTimeColumn(
|
||||||
|
linkify=True,
|
||||||
format=settings.SHORT_DATETIME_FORMAT
|
format=settings.SHORT_DATETIME_FORMAT
|
||||||
)
|
)
|
||||||
kind = ChoiceFieldColumn()
|
kind = ChoiceFieldColumn()
|
||||||
comments = tables.TemplateColumn(
|
comments = tables.TemplateColumn(
|
||||||
template_code='{% load helpers %}{{ value|render_markdown }}'
|
template_code='{% load helpers %}{{ value|render_markdown|truncatewords_html:50 }}'
|
||||||
)
|
)
|
||||||
actions = ButtonsColumn(
|
actions = ButtonsColumn(
|
||||||
model=JournalEntry,
|
model=JournalEntry
|
||||||
buttons=('edit', 'delete')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -128,9 +128,6 @@ class JournalEntryTable(ObjectJournalTable):
|
|||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Object'
|
verbose_name='Object'
|
||||||
)
|
)
|
||||||
comments = tables.TemplateColumn(
|
|
||||||
template_code='{% load helpers %}{{ value|render_markdown|truncatewords_html:50 }}'
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = JournalEntry
|
model = JournalEntry
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from extras import views
|
from extras import views
|
||||||
from extras.models import ConfigContext, Tag
|
from extras.models import ConfigContext, JournalEntry, Tag
|
||||||
|
|
||||||
|
|
||||||
app_name = 'extras'
|
app_name = 'extras'
|
||||||
@ -37,8 +37,10 @@ urlpatterns = [
|
|||||||
path('journal-entries/add/', views.JournalEntryEditView.as_view(), name='journalentry_add'),
|
path('journal-entries/add/', views.JournalEntryEditView.as_view(), name='journalentry_add'),
|
||||||
path('journal-entries/edit/', views.JournalEntryBulkEditView.as_view(), name='journalentry_bulk_edit'),
|
path('journal-entries/edit/', views.JournalEntryBulkEditView.as_view(), name='journalentry_bulk_edit'),
|
||||||
path('journal-entries/delete/', views.JournalEntryBulkDeleteView.as_view(), name='journalentry_bulk_delete'),
|
path('journal-entries/delete/', views.JournalEntryBulkDeleteView.as_view(), name='journalentry_bulk_delete'),
|
||||||
|
path('journal-entries/<int:pk>/', views.JournalEntryView.as_view(), name='journalentry'),
|
||||||
path('journal-entries/<int:pk>/edit/', views.JournalEntryEditView.as_view(), name='journalentry_edit'),
|
path('journal-entries/<int:pk>/edit/', views.JournalEntryEditView.as_view(), name='journalentry_edit'),
|
||||||
path('journal-entries/<int:pk>/delete/', views.JournalEntryDeleteView.as_view(), name='journalentry_delete'),
|
path('journal-entries/<int:pk>/delete/', views.JournalEntryDeleteView.as_view(), name='journalentry_delete'),
|
||||||
|
path('journal-entries/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='journalentry_changelog', kwargs={'model': JournalEntry}),
|
||||||
|
|
||||||
# Change logging
|
# Change logging
|
||||||
path('changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'),
|
path('changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'),
|
||||||
|
@ -306,6 +306,10 @@ class JournalEntryListView(generic.ObjectListView):
|
|||||||
action_buttons = ('export',)
|
action_buttons = ('export',)
|
||||||
|
|
||||||
|
|
||||||
|
class JournalEntryView(generic.ObjectView):
|
||||||
|
queryset = JournalEntry.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class JournalEntryEditView(generic.ObjectEditView):
|
class JournalEntryEditView(generic.ObjectEditView):
|
||||||
queryset = JournalEntry.objects.all()
|
queryset = JournalEntry.objects.all()
|
||||||
model_form = forms.JournalEntryForm
|
model_form = forms.JournalEntryForm
|
||||||
|
57
netbox/templates/extras/journalentry.html
Normal file
57
netbox/templates/extras/journalentry.html
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{% extends 'generic/object.html' %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
<li><a href="{% url 'extras:journalentry_list' %}">Journal Entries</a></li>
|
||||||
|
<li><a href="{% url object.assigned_object|viewname:'journal' pk=object.assigned_object.pk %}">{{ object.assigned_object }}</a></li>
|
||||||
|
<li>{{ object }}</li>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Journal Entry</strong>
|
||||||
|
</div>
|
||||||
|
<table class="table table-hover panel-body attr-table">
|
||||||
|
<tr>
|
||||||
|
<td>Object</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ object.assigned_object.get_absolute_url }}">{{ object.assigned_object }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Created</td>
|
||||||
|
<td>
|
||||||
|
{{ object.created }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Created By</td>
|
||||||
|
<td>
|
||||||
|
{{ object.created_by }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Kind</td>
|
||||||
|
<td>
|
||||||
|
<span class="label label-{{ object.get_kind_class }}">{{ object.get_kind_display }}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Comments</strong>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ object.comments|render_markdown }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user