diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 072506a45..c6f5ecc31 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -146,6 +146,7 @@ Where it is desired to limit the range of available VLANs within a group, users * [#8572](https://github.com/netbox-community/netbox/issues/8572) - Add a `pre_run()` method for reports * [#8593](https://github.com/netbox-community/netbox/issues/8593) - Add a `link` field for contacts * [#8649](https://github.com/netbox-community/netbox/issues/8649) - Enable customization of configuration module using `NETBOX_CONFIGURATION` environment variable +* [#9006](https://github.com/netbox-community/netbox/issues/9006) - Enable custom fields, custom links, and tags for journal entries ### Bug Fixes (From Beta2) @@ -207,11 +208,13 @@ Where it is desired to limit the range of available VLANs within a group, users * Added `data_type` and `object_type` fields * extras.CustomLink * Added `enabled` field +* extras.JournalEntry + * Added `custom_fields` and `tags` fields * ipam.ASN * Added `provider_count` field * ipam.VLANGroup * Added the `/availables-vlans/` endpoint - * Added the `min_vid` and `max_vid` fields + * Added `min_vid` and `max_vid` fields * tenancy.Contact * Added `link` field * virtualization.VMInterface diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 5ab8beb13..e05d4083c 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -14,7 +14,7 @@ from extras.models import * from extras.utils import FeatureQuery from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField from netbox.api.exceptions import SerializerNotFound -from netbox.api.serializers import BaseModelSerializer, ValidatedModelSerializer +from netbox.api.serializers import BaseModelSerializer, NetBoxModelSerializer, ValidatedModelSerializer from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer from tenancy.models import Tenant, TenantGroup from users.api.nested_serializers import NestedUserSerializer @@ -200,7 +200,7 @@ class ImageAttachmentSerializer(ValidatedModelSerializer): # Journal entries # -class JournalEntrySerializer(ValidatedModelSerializer): +class JournalEntrySerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='extras-api:journalentry-detail') assigned_object_type = ContentTypeField( queryset=ContentType.objects.all() @@ -221,7 +221,7 @@ class JournalEntrySerializer(ValidatedModelSerializer): model = JournalEntry fields = [ 'id', 'url', 'display', 'assigned_object_type', 'assigned_object_id', 'assigned_object', 'created', - 'created_by', 'kind', 'comments', + 'created_by', 'kind', 'comments', 'tags', 'custom_fields', ] def validate(self, data): diff --git a/netbox/extras/filtersets.py b/netbox/extras/filtersets.py index 8f231ffb7..25477fbda 100644 --- a/netbox/extras/filtersets.py +++ b/netbox/extras/filtersets.py @@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType from django.db.models import Q from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup -from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet +from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet from tenancy.models import Tenant, TenantGroup from utilities.filters import ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter from virtualization.models import Cluster, ClusterGroup, ClusterType @@ -134,11 +134,7 @@ class ImageAttachmentFilterSet(BaseFilterSet): return queryset.filter(name__icontains=value) -class JournalEntryFilterSet(ChangeLoggedModelFilterSet): - q = django_filters.CharFilter( - method='search', - label='Search', - ) +class JournalEntryFilterSet(NetBoxModelFilterSet): created = django_filters.DateTimeFromToRangeFilter() assigned_object_type = ContentTypeFilter() created_by_id = django_filters.ModelMultipleChoiceFilter( diff --git a/netbox/extras/forms/filtersets.py b/netbox/extras/forms/filtersets.py index e3ae9b1f3..5d66c8be8 100644 --- a/netbox/extras/forms/filtersets.py +++ b/netbox/extras/forms/filtersets.py @@ -7,10 +7,12 @@ from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGrou from extras.choices import * from extras.models import * from extras.utils import FeatureQuery +from netbox.forms.base import NetBoxModelFilterSetForm from tenancy.models import Tenant, TenantGroup from utilities.forms import ( - add_blank_choice, APISelectMultiple, ContentTypeChoiceField, ContentTypeMultipleChoiceField, DateTimePicker, - DynamicModelMultipleChoiceField, FilterForm, MultipleChoiceField, StaticSelect, BOOLEAN_WITH_BLANK_CHOICES, + add_blank_choice, APISelectMultiple, BOOLEAN_WITH_BLANK_CHOICES, ContentTypeChoiceField, + ContentTypeMultipleChoiceField, DateTimePicker, DynamicModelMultipleChoiceField, FilterForm, MultipleChoiceField, + StaticSelect, TagFilterField, ) from virtualization.models import Cluster, ClusterGroup, ClusterType @@ -237,10 +239,10 @@ class LocalConfigContextFilterForm(forms.Form): ) -class JournalEntryFilterForm(FilterForm): +class JournalEntryFilterForm(NetBoxModelFilterSetForm): model = JournalEntry fieldsets = ( - (None, ('q',)), + (None, ('q', 'tag')), ('Creation', ('created_before', 'created_after', 'created_by_id')), ('Attributes', ('assigned_object_type_id', 'kind')) ) @@ -275,6 +277,7 @@ class JournalEntryFilterForm(FilterForm): required=False, widget=StaticSelect() ) + tag = TagFilterField(model) class ObjectChangeFilterForm(FilterForm): diff --git a/netbox/extras/forms/models.py b/netbox/extras/forms/models.py index 78b627662..112911f42 100644 --- a/netbox/extras/forms/models.py +++ b/netbox/extras/forms/models.py @@ -5,6 +5,7 @@ from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGrou from extras.choices import * from extras.models import * from extras.utils import FeatureQuery +from netbox.forms import NetBoxModelForm from tenancy.models import Tenant, TenantGroup from utilities.forms import ( add_blank_choice, BootstrapMixin, CommentField, ContentTypeChoiceField, ContentTypeMultipleChoiceField, @@ -219,18 +220,17 @@ class ImageAttachmentForm(BootstrapMixin, forms.ModelForm): ] -class JournalEntryForm(BootstrapMixin, forms.ModelForm): - comments = CommentField() - +class JournalEntryForm(NetBoxModelForm): kind = forms.ChoiceField( choices=add_blank_choice(JournalEntryKindChoices), required=False, widget=StaticSelect() ) + comments = CommentField() class Meta: model = JournalEntry - fields = ['assigned_object_type', 'assigned_object_id', 'kind', 'comments'] + fields = ['assigned_object_type', 'assigned_object_id', 'kind', 'tags', 'comments'] widgets = { 'assigned_object_type': forms.HiddenInput, 'assigned_object_id': forms.HiddenInput, diff --git a/netbox/extras/graphql/types.py b/netbox/extras/graphql/types.py index c5f34a11e..dbaa0bee2 100644 --- a/netbox/extras/graphql/types.py +++ b/netbox/extras/graphql/types.py @@ -1,4 +1,5 @@ from extras import filtersets, models +from extras.graphql.mixins import CustomFieldsMixin, TagsMixin from netbox.graphql.types import BaseObjectType, ObjectType __all__ = ( @@ -54,7 +55,7 @@ class ImageAttachmentType(BaseObjectType): filterset_class = filtersets.ImageAttachmentFilterSet -class JournalEntryType(ObjectType): +class JournalEntryType(CustomFieldsMixin, TagsMixin, ObjectType): class Meta: model = models.JournalEntry diff --git a/netbox/extras/migrations/0073_journalentry_tags_custom_fields.py b/netbox/extras/migrations/0073_journalentry_tags_custom_fields.py new file mode 100644 index 000000000..73a3e466c --- /dev/null +++ b/netbox/extras/migrations/0073_journalentry_tags_custom_fields.py @@ -0,0 +1,23 @@ +import django.core.serializers.json +from django.db import migrations, models +import taggit.managers + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0072_created_datetimefield'), + ] + + operations = [ + migrations.AddField( + model_name='journalentry', + name='custom_field_data', + field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), + ), + migrations.AddField( + model_name='journalentry', + name='tags', + field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'), + ), + ] diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 5eb5a4a02..ef0ab8b1f 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -19,7 +19,9 @@ from extras.constants import * from extras.conditions import ConditionSet from extras.utils import FeatureQuery, image_upload from netbox.models import ChangeLoggedModel -from netbox.models.features import ExportTemplatesMixin, JobResultsMixin, WebhooksMixin +from netbox.models.features import ( + CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, JobResultsMixin, TagsMixin, WebhooksMixin, +) from utilities.querysets import RestrictedQuerySet from utilities.utils import render_jinja2 @@ -419,7 +421,7 @@ class ImageAttachment(WebhooksMixin, ChangeLoggedModel): return objectchange -class JournalEntry(WebhooksMixin, ChangeLoggedModel): +class JournalEntry(CustomFieldsMixin, CustomLinksMixin, TagsMixin, WebhooksMixin, ChangeLoggedModel): """ 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 diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index 964327558..a13054d56 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -12,7 +12,6 @@ __all__ = ( 'ExportTemplateTable', 'JournalEntryTable', 'ObjectChangeTable', - 'ObjectJournalTable', 'TaggedItemTable', 'TagTable', 'WebhookTable', @@ -210,25 +209,11 @@ class ObjectChangeTable(NetBoxTable): ) -class ObjectJournalTable(NetBoxTable): - """ - Used for displaying a set of JournalEntries within the context of a single object. - """ +class JournalEntryTable(NetBoxTable): created = tables.DateTimeColumn( linkify=True, format=settings.SHORT_DATETIME_FORMAT ) - kind = columns.ChoiceFieldColumn() - comments = tables.TemplateColumn( - template_code='{{ value|markdown|truncatewords_html:50 }}' - ) - - class Meta(NetBoxTable.Meta): - model = JournalEntry - fields = ('id', 'created', 'created_by', 'kind', 'comments', 'actions') - - -class JournalEntryTable(ObjectJournalTable): assigned_object_type = columns.ContentTypeColumn( verbose_name='Object type' ) @@ -237,13 +222,22 @@ class JournalEntryTable(ObjectJournalTable): orderable=False, verbose_name='Object' ) + kind = columns.ChoiceFieldColumn() comments = columns.MarkdownColumn() + comments_short = tables.TemplateColumn( + accessor=tables.A('comments'), + template_code='{{ value|markdown|truncatewords_html:50 }}', + verbose_name='Comments (Short)' + ) + tags = columns.TagColumn( + url_name='extras:journalentry_list' + ) class Meta(NetBoxTable.Meta): model = JournalEntry fields = ( 'pk', 'id', 'created', 'created_by', 'assigned_object_type', 'assigned_object', 'kind', 'comments', - 'actions', + 'comments_short', 'tags', 'actions', ) default_columns = ( 'pk', 'created', 'created_by', 'assigned_object_type', 'assigned_object', 'kind', 'comments' diff --git a/netbox/netbox/views/generic/feature_views.py b/netbox/netbox/views/generic/feature_views.py index 73ced0d65..85e675a69 100644 --- a/netbox/netbox/views/generic/feature_views.py +++ b/netbox/netbox/views/generic/feature_views.py @@ -86,8 +86,10 @@ class ObjectJournalView(View): assigned_object_type=content_type, assigned_object_id=obj.pk ) - journalentry_table = tables.ObjectJournalTable(journalentries) + journalentry_table = tables.JournalEntryTable(journalentries, user=request.user) journalentry_table.configure(request) + journalentry_table.columns.hide('assigned_object_type') + journalentry_table.columns.hide('assigned_object') if request.user.has_perm('extras.add_journalentry'): form = forms.JournalEntryForm( diff --git a/netbox/templates/extras/journalentry.html b/netbox/templates/extras/journalentry.html index 6d1d9599c..d8f8114b5 100644 --- a/netbox/templates/extras/journalentry.html +++ b/netbox/templates/extras/journalentry.html @@ -9,7 +9,7 @@ {% block content %}