diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 7fe4bcacf..34f9c15d1 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -14,6 +14,7 @@ * [#6982](https://github.com/netbox-community/netbox/issues/6982) - Fix styling of empty dropdown list under dark mode * [#6996](https://github.com/netbox-community/netbox/issues/6996) - Global search bar should be full width on mobile * [#7001](https://github.com/netbox-community/netbox/issues/7001) - Fix page focus on load +* [#7034](https://github.com/netbox-community/netbox/issues/7034) - Fix toggling of VLAN group scope selector fields --- diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index 841ea213d..4a4f95213 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -125,6 +125,10 @@ class CustomLinkForm(BootstrapMixin, forms.ModelForm): ('Custom Link', ('name', 'content_type', 'weight', 'group_name', 'button_class', 'new_window')), ('Templates', ('link_text', 'link_url')), ) + widgets = { + 'link_text': forms.Textarea(attrs={'class': 'font-monospace'}), + 'link_url': forms.Textarea(attrs={'class': 'font-monospace'}), + } help_texts = { 'link_text': 'Jinja2 template code for the link text. Reference the object as {{ obj }}. ' 'Links which render as empty text will not be displayed.', @@ -217,6 +221,9 @@ class ExportTemplateForm(BootstrapMixin, forms.ModelForm): ('Template', ('template_code',)), ('Rendering', ('mime_type', 'file_extension', 'as_attachment')), ) + widgets = { + 'template_code': forms.Textarea(attrs={'class': 'font-monospace'}), + } class ExportTemplateCSVForm(CSVModelForm): @@ -316,6 +323,10 @@ class WebhookForm(BootstrapMixin, forms.ModelForm): )), ('SSL', ('ssl_verification', 'ca_file_path')), ) + widgets = { + 'additional_headers': forms.Textarea(attrs={'class': 'font-monospace'}), + 'body_template': forms.Textarea(attrs={'class': 'font-monospace'}), + } class WebhookCSVForm(CSVModelForm): diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 47480ed19..d8e2e11c9 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -35,7 +35,6 @@ class CustomField(ChangeLoggedModel): content_types = models.ManyToManyField( to=ContentType, related_name='custom_fields', - verbose_name='Object(s)', limit_choices_to=FeatureQuery('custom_fields'), help_text='The object(s) to which this field applies.' ) diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index e292f6077..75f5242d3 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -91,7 +91,7 @@ class Webhook(ChangeLoggedModel): blank=True, help_text="User-supplied HTTP headers to be sent with the request in addition to the HTTP content type. " "Headers should be defined in the format Name: Value. Jinja2 template processing is " - "support with the same context as the request body (below)." + "supported with the same context as the request body (below)." ) body_template = models.TextField( blank=True, @@ -249,7 +249,8 @@ class ExportTemplate(ChangeLoggedModel): blank=True ) template_code = models.TextField( - help_text='The list of objects being exported is passed as a context variable named queryset.' + help_text='Jinja2 template code. The list of objects being exported is passed as a context variable named ' + 'queryset.' ) mime_type = models.CharField( max_length=50, diff --git a/netbox/extras/tables.py b/netbox/extras/tables.py index a3c103773..699bffc9f 100644 --- a/netbox/extras/tables.py +++ b/netbox/extras/tables.py @@ -2,7 +2,8 @@ import django_tables2 as tables from django.conf import settings from utilities.tables import ( - BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ColorColumn, ContentTypeColumn, ToggleColumn, + BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ColorColumn, ContentTypeColumn, ContentTypesColumn, + ToggleColumn, ) from .models import * @@ -37,14 +38,16 @@ class CustomFieldTable(BaseTable): name = tables.Column( linkify=True ) + content_types = ContentTypesColumn() required = BooleanColumn() class Meta(BaseTable.Meta): model = CustomField fields = ( - 'pk', 'name', 'label', 'type', 'required', 'weight', 'default', 'description', 'filter_logic', 'choices', + 'pk', 'name', 'content_types', 'label', 'type', 'required', 'weight', 'default', 'description', + 'filter_logic', 'choices', ) - default_columns = ('pk', 'name', 'label', 'type', 'required', 'description') + default_columns = ('pk', 'name', 'content_types', 'label', 'type', 'required', 'description') # @@ -98,19 +101,30 @@ class WebhookTable(BaseTable): name = tables.Column( linkify=True ) + content_types = ContentTypesColumn() enabled = BooleanColumn() - type_create = BooleanColumn() - type_update = BooleanColumn() - type_delete = BooleanColumn() + type_create = BooleanColumn( + verbose_name='Create' + ) + type_update = BooleanColumn( + verbose_name='Update' + ) + type_delete = BooleanColumn( + verbose_name='Delete' + ) + ssl_validation = BooleanColumn( + verbose_name='SSL Validation' + ) class Meta(BaseTable.Meta): model = Webhook fields = ( - 'pk', 'name', 'enabled', 'type_create', 'type_update', 'type_delete', 'http_method', 'payload_url', - 'secret', 'ssl_validation', 'ca_file_path', + 'pk', 'name', 'content_types', 'enabled', 'type_create', 'type_update', 'type_delete', 'http_method', + 'payload_url', 'secret', 'ssl_validation', 'ca_file_path', ) default_columns = ( - 'pk', 'name', 'enabled', 'type_create', 'type_update', 'type_delete', 'http_method', 'payload_url', + 'pk', 'name', 'content_types', 'enabled', 'type_create', 'type_update', 'type_delete', 'http_method', + 'payload_url', ) diff --git a/netbox/templates/extras/customlink.html b/netbox/templates/extras/customlink.html index 34180e4d3..42b2f6118 100644 --- a/netbox/templates/extras/customlink.html +++ b/netbox/templates/extras/customlink.html @@ -11,14 +11,14 @@
- - - - + + + + diff --git a/netbox/templates/extras/exporttemplate.html b/netbox/templates/extras/exporttemplate.html index 2af96c5cb..07b98ee93 100644 --- a/netbox/templates/extras/exporttemplate.html +++ b/netbox/templates/extras/exporttemplate.html @@ -2,6 +2,8 @@ {% load helpers %} {% load plugins %} +{% block title %}{{ object.name }}{% endblock %} + {% block breadcrumbs %} {{ block.super }} diff --git a/netbox/templates/extras/script.html b/netbox/templates/extras/script.html index 6187e6f71..38f82fe03 100644 --- a/netbox/templates/extras/script.html +++ b/netbox/templates/extras/script.html @@ -40,9 +40,11 @@ {% endif %} {% csrf_token %} -
+
{% if form.requires_input %} -

Script Data

+
+
Script Data
+
{% else %}
diff --git a/netbox/templates/extras/script_result.html b/netbox/templates/extras/script_result.html index 78d67e6ac..59ca8a69d 100644 --- a/netbox/templates/extras/script_result.html +++ b/netbox/templates/extras/script_result.html @@ -4,105 +4,113 @@ {% load log_levels %} {% load static %} -{% block title %}{{ script }} {% include 'extras/inc/job_label.html' with result=result %}{% endblock %} - {% block head %} {% endblock %} -{% block content %} -
-
- -
-
-

{{ script.Meta.description|render_markdown }}

- -
-

- Run: {{ result.created|annotated_date }} - {% if result.completed %} - Duration: {{ result.duration }} - {% else %} -

- Loading... -
- {% endif %} -

-
- {% if result.completed %} -
-
-
-
- Script Log -
-
-
Content Type{{ object.content_type }}
Name {{ object.name }}
Content Type{{ object.content_type }}
Group Name {{ object.group_name|placeholder }}
- - - - - - {% for log in result.data.log %} - - - - - - {% empty %} - - - - {% endfor %} -
LineLevelMessage
{{ forloop.counter }}{% log_level log.status %}{{ log.message|render_markdown }}
- No log output -
-
- {% if execution_time %} - - {% endif %} - - - - {% else %} -
-
-
Pending Results
-
-
- {% endif %} - -
-
{{ result.data.output }}
-
-
-

{{ script.filename }}

-
{{ script.source }}
-
- +{% block title %}{{ script }}{% endblock %} + +{% block subtitle %} + {{ script.Meta.description|render_markdown }} + {% include 'extras/inc/job_label.html' with result=result %} {% endblock %} +{% block header %} +
+
+ +
+
+ {{ block.super }} +{% endblock header %} + +{% block content-wrapper %} + +
+

+ Run: {{ result.created|annotated_date }} + {% if result.completed %} + Duration: {{ result.duration }} + {% else %} +

+ Loading... +
+ {% endif %} +

+
+ {% if result.completed %} +
+
+
+
+ Script Log +
+
+ + + + + + + {% for log in result.data.log %} + + + + + + {% empty %} + + + + {% endfor %} +
LineLevelMessage
{{ forloop.counter }}{% log_level log.status %}{{ log.message|render_markdown }}
+ No log output +
+
+ {% if execution_time %} + + {% endif %} +
+
+
+ {% else %} +
+
+
Pending Results
+
+
+ {% endif %} +
+
+
{{ result.data.output }}
+
+
+

{{ script.filename }}

+
{{ script.source }}
+
+
+{% endblock content-wrapper %} + {% block data %} diff --git a/netbox/utilities/tables.py b/netbox/utilities/tables.py index 120330520..26a16c171 100644 --- a/netbox/utilities/tables.py +++ b/netbox/utilities/tables.py @@ -12,6 +12,8 @@ from django_tables2.data import TableQuerysetData from django_tables2.utils import Accessor from extras.models import CustomField +from extras.utils import FeatureQuery +from .utils import content_type_name from .paginator import EnhancedPaginator, get_paginate_count @@ -235,12 +237,20 @@ class ContentTypeColumn(tables.Column): Display a ContentType instance. """ def render(self, value): - return value.name[0].upper() + value.name[1:] + return content_type_name(value) def value(self, value): return f"{value.app_label}.{value.model}" +class ContentTypesColumn(tables.ManyToManyColumn): + """ + Display a list of ContentType instances. + """ + def transform(self, obj): + return content_type_name(obj) + + class ColorColumn(tables.Column): """ Display a color (#RRGGBB).