1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Merge branch 'develop' into feature

This commit is contained in:
jeremystretch
2021-11-12 09:09:15 -05:00
15 changed files with 56 additions and 25 deletions

View File

@ -14,7 +14,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v3.0.9 placeholder: v3.0.10
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -14,7 +14,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v3.0.9 placeholder: v3.0.10
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -1,20 +1,32 @@
# NetBox v3.0 # NetBox v3.0
## v3.0.10 (FUTURE) ## v3.0.11 (FUTURE)
---
## v3.0.10 (2021-11-12)
### Enhancements ### Enhancements
* [#7740](https://github.com/netbox-community/netbox/issues/7740) - Add mini-DIN 8 console port type * [#7740](https://github.com/netbox-community/netbox/issues/7740) - Add mini-DIN 8 console port type
* [#7760](https://github.com/netbox-community/netbox/issues/7760) - Add `vid` filter field to VLANs list * [#7760](https://github.com/netbox-community/netbox/issues/7760) - Add `vid` filter field to VLANs list
* [#7767](https://github.com/netbox-community/netbox/issues/7767) - Add visual aids to interfaces table for type, enabled status
### Bug Fixes ### Bug Fixes
* [#7564](https://github.com/netbox-community/netbox/issues/7564) - Fix assignment of members to virtual chassis with initial position of zero
* [#7701](https://github.com/netbox-community/netbox/issues/7701) - Fix conflation of assigned IP status & role in interface tables * [#7701](https://github.com/netbox-community/netbox/issues/7701) - Fix conflation of assigned IP status & role in interface tables
* [#7741](https://github.com/netbox-community/netbox/issues/7741) - Fix 404 when attaching multiple images in succession * [#7741](https://github.com/netbox-community/netbox/issues/7741) - Fix 404 when attaching multiple images in succession
* [#7752](https://github.com/netbox-community/netbox/issues/7752) - Fix minimum version check under Python v3.10 * [#7752](https://github.com/netbox-community/netbox/issues/7752) - Fix minimum version check under Python v3.10
* [#7766](https://github.com/netbox-community/netbox/issues/7766) - Add missing outer dimension columns to rack table * [#7766](https://github.com/netbox-community/netbox/issues/7766) - Add missing outer dimension columns to rack table
* [#7780](https://github.com/netbox-community/netbox/issues/7780) - Preserve mutli-line values during CSV file import * [#7780](https://github.com/netbox-community/netbox/issues/7780) - Preserve multi-line values during CSV file import
* [#7783](https://github.com/netbox-community/netbox/issues/7783) - Fix indentation of locations under site view * [#7783](https://github.com/netbox-community/netbox/issues/7783) - Fix indentation of locations under site view
* [#7788](https://github.com/netbox-community/netbox/issues/7788) - Improve XSS mitigation in Markdown renderer
* [#7791](https://github.com/netbox-community/netbox/issues/7791) - Enable sorting device bays table by installed device status
* [#7802](https://github.com/netbox-community/netbox/issues/7802) - Differentiate ID and VID columns in VLANs table
* [#7808](https://github.com/netbox-community/netbox/issues/7808) - Fix reference values for content type under custom field import form
* [#7809](https://github.com/netbox-community/netbox/issues/7809) - Add missing export template support for various models
* [#7814](https://github.com/netbox-community/netbox/issues/7814) - Fix restriction of user & group objects in GraphQL API queries
--- ---

View File

@ -118,12 +118,18 @@ class VirtualChassisCreateForm(BootstrapMixin, CustomFieldModelForm):
'name', 'domain', 'region', 'site_group', 'site', 'rack', 'members', 'initial_position', 'tags', 'name', 'domain', 'region', 'site_group', 'site', 'rack', 'members', 'initial_position', 'tags',
] ]
def clean(self):
if self.cleaned_data['members'] and self.cleaned_data['initial_position'] is None:
raise forms.ValidationError({
'initial_position': "A position must be specified for the first VC member."
})
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
instance = super().save(*args, **kwargs) instance = super().save(*args, **kwargs)
# Assign VC members # Assign VC members
if instance.pk: if instance.pk and self.cleaned_data['members']:
initial_position = self.cleaned_data.get('initial_position') or 1 initial_position = self.cleaned_data.get('initial_position', 1)
for i, member in enumerate(self.cleaned_data['members'], start=initial_position): for i, member in enumerate(self.cleaned_data['members'], start=initial_position):
member.virtual_chassis = instance member.virtual_chassis = instance
member.vc_position = i member.vc_position = i

View File

@ -49,6 +49,14 @@ def get_cabletermination_row_class(record):
return '' return ''
def get_interface_row_class(record):
if not record.enabled:
return 'danger'
elif record.is_virtual:
return 'primary'
return get_cabletermination_row_class(record)
def get_interface_state_attribute(record): def get_interface_state_attribute(record):
""" """
Get interface enabled state as string to attach to <tr/> DOM element. Get interface enabled state as string to attach to <tr/> DOM element.
@ -508,7 +516,7 @@ class InterfaceTable(DeviceComponentTable, BaseInterfaceTable, PathEndpointTable
class DeviceInterfaceTable(InterfaceTable): class DeviceInterfaceTable(InterfaceTable):
name = tables.TemplateColumn( name = tables.TemplateColumn(
template_code='<i class="mdi mdi-{% if record.mgmt_only %}wrench{% elif record.is_lag %}drag-horizontal-variant' template_code='<i class="mdi mdi-{% if record.mgmt_only %}wrench{% elif record.is_lag %}reorder-horizontal'
'{% elif record.is_virtual %}circle{% elif record.is_wireless %}wifi{% else %}ethernet' '{% elif record.is_virtual %}circle{% elif record.is_wireless %}wifi{% else %}ethernet'
'{% endif %}"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>', '{% endif %}"></i> <a href="{{ record.get_absolute_url }}">{{ value }}</a>',
order_by=Accessor('_name'), order_by=Accessor('_name'),
@ -544,7 +552,7 @@ class DeviceInterfaceTable(InterfaceTable):
'cable', 'connection', 'actions', 'cable', 'connection', 'actions',
) )
row_attrs = { row_attrs = {
'class': get_cabletermination_row_class, 'class': get_interface_row_class,
'data-name': lambda record: record.name, 'data-name': lambda record: record.name,
'data-enabled': get_interface_state_attribute, 'data-enabled': get_interface_state_attribute,
} }
@ -663,7 +671,8 @@ class DeviceBayTable(DeviceComponentTable):
} }
) )
status = tables.TemplateColumn( status = tables.TemplateColumn(
template_code=DEVICEBAY_STATUS template_code=DEVICEBAY_STATUS,
order_by=Accessor('installed_device__status')
) )
installed_device = tables.Column( installed_device = tables.Column(
linkify=True linkify=True

View File

@ -70,7 +70,7 @@ class CustomLinkForm(BootstrapMixin, forms.ModelForm):
class ExportTemplateForm(BootstrapMixin, forms.ModelForm): class ExportTemplateForm(BootstrapMixin, forms.ModelForm):
content_type = ContentTypeChoiceField( content_type = ContentTypeChoiceField(
queryset=ContentType.objects.all(), queryset=ContentType.objects.all(),
limit_choices_to=FeatureQuery('custom_links') limit_choices_to=FeatureQuery('export_templates')
) )
class Meta: class Meta:

View File

@ -33,7 +33,7 @@ class CustomFieldManager(models.Manager.from_queryset(RestrictedQuerySet)):
return self.get_queryset().filter(content_types=content_type) return self.get_queryset().filter(content_types=content_type)
@extras_features('webhooks') @extras_features('webhooks', 'export_templates')
class CustomField(ChangeLoggedModel): class CustomField(ChangeLoggedModel):
content_types = models.ManyToManyField( content_types = models.ManyToManyField(
to=ContentType, to=ContentType,

View File

@ -35,7 +35,7 @@ __all__ = (
) )
@extras_features('webhooks') @extras_features('webhooks', 'export_templates')
class Webhook(ChangeLoggedModel): class Webhook(ChangeLoggedModel):
""" """
A Webhook defines a request that will be sent to a remote application when an object is created, updated, and/or A Webhook defines a request that will be sent to a remote application when an object is created, updated, and/or
@ -177,7 +177,7 @@ class Webhook(ChangeLoggedModel):
return json.dumps(context, cls=JSONEncoder) return json.dumps(context, cls=JSONEncoder)
@extras_features('webhooks') @extras_features('webhooks', 'export_templates')
class CustomLink(ChangeLoggedModel): class CustomLink(ChangeLoggedModel):
""" """
A custom link to an external representation of a NetBox object. The link text and URL fields accept Jinja2 template A custom link to an external representation of a NetBox object. The link text and URL fields accept Jinja2 template
@ -230,7 +230,7 @@ class CustomLink(ChangeLoggedModel):
return reverse('extras:customlink', args=[self.pk]) return reverse('extras:customlink', args=[self.pk])
@extras_features('webhooks') @extras_features('webhooks', 'export_templates')
class ExportTemplate(ChangeLoggedModel): class ExportTemplate(ChangeLoggedModel):
content_type = models.ForeignKey( content_type = models.ForeignKey(
to=ContentType, to=ContentType,

View File

@ -13,7 +13,7 @@ from utilities.fields import ColorField
# Tags # Tags
# #
@extras_features('webhooks') @extras_features('webhooks', 'export_templates')
class Tag(ChangeLoggedModel, TagBase): class Tag(ChangeLoggedModel, TagBase):
color = ColorField( color = ColorField(
default=ColorChoices.COLOR_GREY default=ColorChoices.COLOR_GREY

View File

@ -96,7 +96,7 @@ class VLANTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
vid = tables.TemplateColumn( vid = tables.TemplateColumn(
template_code=VLAN_LINK, template_code=VLAN_LINK,
verbose_name='ID' verbose_name='VID'
) )
site = tables.Column( site = tables.Column(
linkify=True linkify=True

View File

@ -61,7 +61,7 @@
<a href="{{ vc_member.get_absolute_url }}">{{ vc_member }}</a> <a href="{{ vc_member.get_absolute_url }}">{{ vc_member }}</a>
</td> </td>
<td> <td>
{% badge vc_member.vc_position %} {% badge vc_member.vc_position show_empty=True %}
</td> </td>
<td> <td>
{% if object.master == vc_member %} {% if object.master == vc_member %}

View File

@ -19,7 +19,7 @@ class GroupType(DjangoObjectType):
@classmethod @classmethod
def get_queryset(cls, queryset, info): def get_queryset(cls, queryset, info):
return RestrictedQuerySet(model=Group) return RestrictedQuerySet(model=Group).restrict(info.context.user, 'view')
class UserType(DjangoObjectType): class UserType(DjangoObjectType):
@ -34,4 +34,4 @@ class UserType(DjangoObjectType):
@classmethod @classmethod
def get_queryset(cls, queryset, info): def get_queryset(cls, queryset, info):
return RestrictedQuerySet(model=User) return RestrictedQuerySet(model=User).restrict(info.context.user, 'view')

View File

@ -328,7 +328,7 @@ class CSVMultipleContentTypeField(forms.ModelMultipleChoiceField):
app_label, model = name.split('.') app_label, model = name.split('.')
ct_filter |= Q(app_label=app_label, model=model) ct_filter |= Q(app_label=app_label, model=model)
return list(ContentType.objects.filter(ct_filter).values_list('pk', flat=True)) return list(ContentType.objects.filter(ct_filter).values_list('pk', flat=True))
return super().prepare_value(value) return f'{value.app_label}.{value.model}'
# #

View File

@ -15,7 +15,6 @@ from django.utils.html import strip_tags
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from markdown import markdown from markdown import markdown
from netbox.config import get_config
from utilities.forms import get_selected_values, TableConfigForm from utilities.forms import get_selected_values, TableConfigForm
from utilities.utils import foreground_color from utilities.utils import foreground_color
@ -42,14 +41,19 @@ def render_markdown(value):
""" """
Render text as Markdown Render text as Markdown
""" """
schemes = '|'.join(settings.ALLOWED_URL_SCHEMES)
# Strip HTML tags # Strip HTML tags
value = strip_tags(value) value = strip_tags(value)
# Sanitize Markdown links # Sanitize Markdown links
schemes = '|'.join(get_config().ALLOWED_URL_SCHEMES) pattern = fr'\[([^\]]+)\]\((?!({schemes})).*:(.+)\)'
pattern = fr'\[(.+)\]\((?!({schemes})).*:(.+)\)'
value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE) value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE)
# Sanitize Markdown reference links
pattern = fr'\[(.+)\]:\w?(?!({schemes})).*:(.+)'
value = re.sub(pattern, '[\\1]: \\3', value, flags=re.IGNORECASE)
# Render Markdown # Render Markdown
html = markdown(value, extensions=['fenced_code', 'tables']) html = markdown(value, extensions=['fenced_code', 'tables'])

View File

@ -15,13 +15,13 @@ djangorestframework==3.12.4
drf-yasg[validation]==1.20.0 drf-yasg[validation]==1.20.0
graphene_django==2.15.0 graphene_django==2.15.0
gunicorn==20.1.0 gunicorn==20.1.0
Jinja2==3.0.2 Jinja2==3.0.3
Markdown==3.3.4 Markdown==3.3.4
markdown-include==0.6.0 markdown-include==0.6.0
mkdocs-material==7.3.6 mkdocs-material==7.3.6
netaddr==0.8.0 netaddr==0.8.0
Pillow==8.4.0 Pillow==8.4.0
psycopg2-binary==2.9.1 psycopg2-binary==2.9.2
PyYAML==6.0 PyYAML==6.0
social-auth-app-django==5.0.0 social-auth-app-django==5.0.0
social-auth-core==4.1.0 social-auth-core==4.1.0