Merge branch 'netbox-community:develop' into develop

This commit is contained in:
rmanyari 2023-03-09 14:32:47 -07:00 committed by GitHub
commit d9a1f5d06a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 153 additions and 50 deletions

View File

@ -13,9 +13,9 @@ NetBox provides the ideal "source of truth" to power network automation.
Available as open source software under the Apache 2.0 license, NetBox serves
as the cornerstone for network automation in thousands of organizations.
* **Physical infrasucture:** Accurately model the physical world, from global regions down to individual racks of gear. Then connect everything - network, console, and power!
* **Physical infrastructure:** Accurately model the physical world, from global regions down to individual racks of gear. Then connect everything - network, console, and power!
* **Modern IPAM:** All the standard IPAM functionality you expect, plus VRF import/export tracking, VLAN management, and overlay support.
* **Data circuits:** Confidently manage the delivery of crtical circuits from various service providers, modeled seamlessly alongside your own infrastructure.
* **Data circuits:** Confidently manage the delivery of critical circuits from various service providers, modeled seamlessly alongside your own infrastructure.
* **Power tracking:** Map the distribution of power from upstream sources to individual feeds and outlets.
* **Organization:** Manage tenant and contact assignments natively.
* **Powerful search:** Easily find anything you need using a single global search function.

View File

@ -6,14 +6,20 @@
* [#10058](https://github.com/netbox-community/netbox/issues/10058) - Enable searching for devices/VMs by primary IP address
* [#11011](https://github.com/netbox-community/netbox/issues/11011) - Add ability to toggle visibility of virtual interfaces under device view
* [#11294](https://github.com/netbox-community/netbox/issues/11294) - Enable live preview of Markdown content
* [#11807](https://github.com/netbox-community/netbox/issues/11807) - Restore default page size when navigating between views
* [#11817](https://github.com/netbox-community/netbox/issues/11817) - Add `connected_endpoints` field to GraphQL API for cabled objects
* [#11862](https://github.com/netbox-community/netbox/issues/11862) - Add Cisco StackWise 1T interface type
* [#11871](https://github.com/netbox-community/netbox/issues/11871) - Add IEEE 802.3az PoE type for interfaces
### Bug Fixes
* [#11470](https://github.com/netbox-community/netbox/issues/11470) - Avoid raising exception when filtering IPs by an invalid address
* [#11565](https://github.com/netbox-community/netbox/issues/11565) - Apply custom field defaults to IP address created during FHRP group creation
* [#11758](https://github.com/netbox-community/netbox/issues/11758) - Support non-URL-safe characters in plugin menu titles
* [#11796](https://github.com/netbox-community/netbox/issues/11796) - When importing devices, restrict rack by location only if the location field is specified
* [#11819](https://github.com/netbox-community/netbox/issues/11819) - Fix filtering of cable terminations by object type
* [#11903](https://github.com/netbox-community/netbox/issues/11903) - Fix escaping of return URL values for action buttons in tables
---

View File

@ -7,7 +7,7 @@ from ipam.models import ASN
from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import (
add_blank_choice, CommentField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SmallTextarea,
add_blank_choice, CommentField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
StaticSelect,
)
@ -35,7 +35,6 @@ class ProviderBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label=_('Comments')
)
@ -63,7 +62,6 @@ class ProviderNetworkBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label=_('Comments')
)
@ -125,7 +123,6 @@ class CircuitBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label=_('Comments')
)

View File

@ -902,6 +902,7 @@ class InterfaceTypeChoices(ChoiceSet):
TYPE_STACKWISE160 = 'cisco-stackwise-160'
TYPE_STACKWISE320 = 'cisco-stackwise-320'
TYPE_STACKWISE480 = 'cisco-stackwise-480'
TYPE_STACKWISE1T = 'cisco-stackwise-1t'
TYPE_JUNIPER_VCP = 'juniper-vcp'
TYPE_SUMMITSTACK = 'extreme-summitstack'
TYPE_SUMMITSTACK128 = 'extreme-summitstack-128'
@ -1078,6 +1079,7 @@ class InterfaceTypeChoices(ChoiceSet):
(TYPE_STACKWISE160, 'Cisco StackWise-160'),
(TYPE_STACKWISE320, 'Cisco StackWise-320'),
(TYPE_STACKWISE480, 'Cisco StackWise-480'),
(TYPE_STACKWISE1T, 'Cisco StackWise-1T'),
(TYPE_JUNIPER_VCP, 'Juniper VCP'),
(TYPE_SUMMITSTACK, 'Extreme SummitStack'),
(TYPE_SUMMITSTACK128, 'Extreme SummitStack-128'),
@ -1135,6 +1137,7 @@ class InterfacePoETypeChoices(ChoiceSet):
TYPE_1_8023AF = 'type1-ieee802.3af'
TYPE_2_8023AT = 'type2-ieee802.3at'
TYPE_2_8023AZ = 'type2-ieee802.3az'
TYPE_3_8023BT = 'type3-ieee802.3bt'
TYPE_4_8023BT = 'type4-ieee802.3bt'
@ -1149,6 +1152,7 @@ class InterfacePoETypeChoices(ChoiceSet):
(
(TYPE_1_8023AF, '802.3af (Type 1)'),
(TYPE_2_8023AT, '802.3at (Type 2)'),
(TYPE_2_8023AZ, '802.3az (Type 2)'),
(TYPE_3_8023BT, '802.3bt (Type 3)'),
(TYPE_4_8023BT, '802.3bt (Type 4)'),
)

View File

@ -1727,6 +1727,7 @@ class CableFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
class CableTerminationFilterSet(BaseFilterSet):
termination_type = ContentTypeFilter()
class Meta:
model = CableTermination

View File

@ -11,7 +11,7 @@ from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import (
add_blank_choice, BulkEditForm, BulkEditNullBooleanSelect, ColorField, CommentField, DynamicModelChoiceField,
DynamicModelMultipleChoiceField, form_from_model, SmallTextarea, StaticSelect, SelectSpeedWidget,
DynamicModelMultipleChoiceField, form_from_model, StaticSelect, SelectSpeedWidget
)
__all__ = (
@ -138,7 +138,6 @@ class SiteBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -309,7 +308,6 @@ class RackBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -345,7 +343,6 @@ class RackReservationBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -406,7 +403,6 @@ class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -441,7 +437,6 @@ class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -551,7 +546,6 @@ class DeviceBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -594,7 +588,6 @@ class ModuleBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -644,7 +637,6 @@ class CableBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -668,7 +660,6 @@ class VirtualChassisBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -714,7 +705,6 @@ class PowerPanelBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -776,7 +766,6 @@ class PowerFeedBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label=_('Comments')
)

View File

@ -2,6 +2,7 @@ from .model_forms import *
from .filtersets import *
from .bulk_edit import *
from .bulk_import import *
from .misc import *
from .mixins import *
from .config import *
from .scripts import *

View File

@ -0,0 +1,14 @@
from django import forms
__all__ = (
'RenderMarkdownForm',
)
class RenderMarkdownForm(forms.Form):
"""
Provides basic validation for markup to be rendered.
"""
text = forms.CharField(
required=False
)

View File

@ -92,4 +92,6 @@ urlpatterns = [
path('scripts/results/<int:job_result_pk>/', views.ScriptResultView.as_view(), name='script_result'),
re_path(r'^scripts/(?P<module>.([^.]+)).(?P<name>.(.+))/', views.ScriptView.as_view(), name='script'),
# Markdown
path('render/markdown/', views.RenderMarkdownView.as_view(), name="render_markdown")
]

View File

@ -1,7 +1,7 @@
from django.contrib import messages
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count, Q
from django.http import Http404, HttpResponseForbidden
from django.http import Http404, HttpResponseBadRequest, HttpResponseForbidden, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.views.generic import View
@ -10,6 +10,7 @@ from rq import Worker
from netbox.views import generic
from utilities.htmx import is_htmx
from utilities.templatetags.builtins.filters import render_markdown
from utilities.utils import copy_safe_request, count_related, get_viewname, normalize_querydict, shallow_compare_dict
from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
from . import filtersets, forms, tables
@ -885,3 +886,18 @@ class JobResultBulkDeleteView(generic.BulkDeleteView):
queryset = JobResult.objects.all()
filterset = filtersets.JobResultFilterSet
table = tables.JobResultTable
#
# Markdown
#
class RenderMarkdownView(View):
def post(self, request):
form = forms.RenderMarkdownForm(request.POST)
if not form.is_valid():
HttpResponseBadRequest()
rendered = render_markdown(form.cleaned_data['text'])
return HttpResponse(rendered)

View File

@ -10,7 +10,7 @@ from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import (
add_blank_choice, BulkEditNullBooleanSelect, CommentField, DynamicModelChoiceField, NumericArrayField,
SmallTextarea, StaticSelect, DynamicModelMultipleChoiceField,
StaticSelect, DynamicModelMultipleChoiceField
)
__all__ = (
@ -48,7 +48,6 @@ class VRFBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -69,7 +68,6 @@ class RouteTargetBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -116,7 +114,6 @@ class ASNBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -145,7 +142,6 @@ class AggregateBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -227,7 +223,6 @@ class PrefixBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -266,7 +261,6 @@ class IPRangeBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -314,7 +308,6 @@ class IPAddressBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -359,7 +352,6 @@ class FHRPGroupBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -442,7 +434,6 @@ class VLANBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -474,7 +465,6 @@ class ServiceTemplateBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -504,7 +494,6 @@ class L2VPNBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)

View File

@ -1,5 +1,6 @@
from dataclasses import dataclass
from typing import Optional
from urllib.parse import quote
import django_tables2 as tables
from django.conf import settings
@ -8,7 +9,6 @@ from django.db.models import DateField, DateTimeField
from django.template import Context, Template
from django.urls import reverse
from django.utils.dateparse import parse_date
from django.utils.encoding import escape_uri_path
from django.utils.html import escape
from django.utils.formats import date_format
from django.utils.safestring import mark_safe
@ -235,7 +235,7 @@ class ActionsColumn(tables.Column):
model = table.Meta.model
request = getattr(table, 'context', {}).get('request')
url_appendix = f'?return_url={escape_uri_path(request.get_full_path())}' if request else ''
url_appendix = f'?return_url={quote(request.get_full_path())}' if request else ''
html = ''
# Compile actions menu

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -4,6 +4,7 @@ import { initMoveButtons } from './moveOptions';
import { initReslug } from './reslug';
import { initSelectAll } from './selectAll';
import { initSelectMultiple } from './selectMultiple';
import { initMarkdownPreviews } from './markdownPreview';
export function initButtons(): void {
for (const func of [
@ -13,6 +14,7 @@ export function initButtons(): void {
initSelectAll,
initSelectMultiple,
initMoveButtons,
initMarkdownPreviews,
]) {
func();
}

View File

@ -0,0 +1,45 @@
import { isTruthy } from 'src/util';
/**
* interface for htmx configRequest event
*/
declare global {
interface HTMLElementEventMap {
'htmx:configRequest': CustomEvent<{
parameters: Record<string, string>;
headers: Record<string, string>;
}>;
}
}
function initMarkdownPreview(markdownWidget: HTMLDivElement) {
const previewButton = markdownWidget.querySelector('button.preview-button') as HTMLButtonElement;
const textarea = markdownWidget.querySelector('textarea') as HTMLTextAreaElement;
const preview = markdownWidget.querySelector('div.preview') as HTMLDivElement;
/**
* Make sure the textarea has style attribute height
* So that it can be copied over to preview div.
*/
if (!isTruthy(textarea.style.height)) {
const { height } = textarea.getBoundingClientRect();
textarea.style.height = `${height}px`;
}
/**
* Add the value of the textarea to the body of the htmx request
* and copy the height of text are to the preview div
*/
previewButton.addEventListener('htmx:configRequest', e => {
e.detail.parameters = { text: textarea.value || '' };
e.detail.headers['X-CSRFToken'] = window.CSRF_TOKEN;
preview.style.minHeight = textarea.style.height;
preview.innerHTML = '';
});
}
export function initMarkdownPreviews(): void {
for (const markdownWidget of document.querySelectorAll<HTMLDivElement>('.markdown-widget')) {
initMarkdownPreview(markdownWidget);
}
}

View File

@ -236,12 +236,12 @@ table {
}
th.asc > a::after {
content: "\f0140";
content: '\f0140';
font-family: 'Material Design Icons';
}
th.desc > a::after {
content: "\f0143";
content: '\f0143';
font-family: 'Material Design Icons';
}
@ -416,18 +416,18 @@ nav.search {
}
}
// Styles for the quicksearch and its clear button;
// Styles for the quicksearch and its clear button;
// Overrides input-group styles and adds transition effects
.quicksearch {
input[type="search"] {
border-radius: $border-radius !important;
input[type='search'] {
border-radius: $border-radius !important;
}
button {
margin-left: -32px !important;
z-index: 100 !important;
outline: none !important;
border-radius: $border-radius !important;
border-radius: $border-radius !important;
transition: visibility 0s, opacity 0.2s linear;
}
@ -998,9 +998,24 @@ div.card-overlay {
padding: 8px;
}
/* Markdown widget */
.markdown-widget {
.nav-link {
border-bottom: 0;
&.active {
background-color: var(--nbx-body-bg);
}
}
.nav-tabs {
background-color: var(--nbx-pre-bg);
}
}
// Preformatted text blocks
td pre {
margin-bottom: 0
margin-bottom: 0;
}
pre.block {
padding: $spacer;

View File

@ -2,7 +2,7 @@ from django import forms
from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import *
from utilities.forms import CommentField, DynamicModelChoiceField, SmallTextarea
from utilities.forms import CommentField, DynamicModelChoiceField
__all__ = (
'ContactBulkEditForm',
@ -106,7 +106,6 @@ class ContactBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)

View File

@ -27,7 +27,7 @@ class CommentField(forms.CharField):
"""
A textarea with support for Markdown rendering. Exists mostly just to add a standard `help_text`.
"""
widget = forms.Textarea
widget = widgets.MarkdownWidget
help_text = f"""
<i class="mdi mdi-information-outline"></i>
<a href="{static('docs/reference/markdown/')}" target="_blank" tabindex="-1">

View File

@ -16,6 +16,7 @@ __all__ = (
'ColorSelect',
'DatePicker',
'DateTimePicker',
'MarkdownWidget',
'NumericArrayField',
'SelectDurationWidget',
'SelectSpeedWidget',
@ -116,6 +117,10 @@ class SelectDurationWidget(forms.NumberInput):
template_name = 'widgets/select_duration.html'
class MarkdownWidget(forms.Textarea):
template_name = 'widgets/markdown_input.html'
class NumericArrayField(SimpleArrayField):
def clean(self, value):

View File

@ -6,7 +6,7 @@
{# Render the field label, except for: #}
{# 1. Checkboxes (label appears to the right of the field #}
{# 2. Textareas with no label set (will expand across entire row) #}
{% if field|widget_type == 'checkboxinput' or field|widget_type == 'textarea' and not label %}
{% if field|widget_type == 'checkboxinput' or field|widget_type == 'textarea' or field|widget_type == 'markdownwidget' and not label %}
{% else %}
<label for="{{ field.id_for_label }}" class="col-sm-3 col-form-label text-lg-end{% if field.field.required %} required{% endif %}">
{{ label }}

View File

@ -0,0 +1,22 @@
<div class="border rounded markdown-widget">
<ul class="nav nav-tabs px-3 pt-2 rounded-top border-0">
<li class="nav-item" role="presentation">
<button class="nav-link active " id="{{ widget.name }}-input-tab" data-bs-toggle="tab" data-bs-target="#{{ widget.name }}-input" type="button" role="tab" aria-controls="{{ widget.name }}-input" aria-selected="true">
Write
</button>
</li>
<li class="nav-item" role="presentation">
<button hx-target="#{{ widget.name }}-preview" hx-swap="innerHTML" hx-post="{% url 'extras:render_markdown' %}" class="nav-link preview-button" id="{{ widget.name }}-markdown-preview-tab" data-bs-toggle="tab" data-bs-target="#{{ widget.name }}-markdown-preview" type="button" role="tab" aria-controls="{{ widget.name }}-markdown-preview" aria-selected="false">
Preview
</button>
</li>
</ul>
<div class="tab-content bg-body rounded-bottom border-top">
<div class="tab-pane show active" id="{{ widget.name }}-input" role="tabpanel" aria-labelledby="{{ widget.name }}-input-tab">
{% include "django/forms/widgets/textarea.html" %}
</div>
<div class="tab-pane show" id="{{ widget.name }}-markdown-preview" role="tabpanel" aria-labelledby="{{ widget.name }}-markdown-preview-tab">
<div id="{{ widget.name }}-preview" class="preview px-3 py-2">Testing</div>
</div>
</div>
</div>

View File

@ -9,7 +9,7 @@ from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import (
add_blank_choice, BulkEditNullBooleanSelect, BulkRenameForm, CommentField, DynamicModelChoiceField,
DynamicModelMultipleChoiceField, SmallTextarea, StaticSelect
DynamicModelMultipleChoiceField, StaticSelect
)
from virtualization.choices import *
from virtualization.models import *
@ -90,7 +90,6 @@ class ClusterBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label=_('Comments')
)
@ -163,7 +162,6 @@ class VirtualMachineBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label=_('Comments')
)

View File

@ -5,7 +5,7 @@ from dcim.choices import LinkStatusChoices
from ipam.models import VLAN
from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import add_blank_choice, CommentField, DynamicModelChoiceField, SmallTextarea
from utilities.forms import add_blank_choice, CommentField, DynamicModelChoiceField
from wireless.choices import *
from wireless.constants import SSID_MAX_LENGTH
from wireless.models import *
@ -74,7 +74,6 @@ class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)
@ -119,7 +118,6 @@ class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
required=False
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
)