Closes #6150: Enable change logging for journal entries

This commit is contained in:
jeremystretch 2021-04-13 10:53:55 -04:00
parent e5bbf47ab9
commit a296a9e109
7 changed files with 77 additions and 11 deletions

View File

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

View File

@ -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')),

View File

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

View File

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

View File

@ -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'),

View File

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

View 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 %}