Merge branch 'develop' into feature

This commit is contained in:
jeremystretch
2023-03-13 11:58:37 -04:00
63 changed files with 502 additions and 212 deletions

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

@@ -183,7 +183,7 @@ class ImportForm(BootstrapMixin, forms.Form):
if 'data_file' in self.files:
self.data_field = 'data_file'
file = self.files.get('data_file')
data = file.read().decode('utf-8')
data = file.read().decode('utf-8-sig')
else:
data = self.cleaned_data['data']

View File

@@ -211,6 +211,7 @@ def parse_csv(reader):
# `site.slug` header, to indicate the related site is being referenced by its slug.
for header in next(reader):
header = header.strip()
if '.' in header:
field, to_field = header.split('.', 1)
headers[field] = to_field

View File

@@ -16,6 +16,7 @@ __all__ = (
'ColorSelect',
'DatePicker',
'DateTimePicker',
'MarkdownWidget',
'NumericArrayField',
'SelectDurationWidget',
'SelectSpeedWidget',
@@ -87,6 +88,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

@@ -76,8 +76,6 @@ def get_paginate_count(request):
if 'per_page' in request.GET:
try:
per_page = int(request.GET.get('per_page'))
if request.user.is_authenticated:
request.user.config.set('pagination.per_page', per_page, commit=True)
return _max_allowed(per_page)
except ValueError:
pass

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

@@ -17,6 +17,8 @@ from utilities.api import get_graphql_type_for_model
from .base import ModelTestCase
from .utils import disable_warnings
from ipam.graphql.types import IPAddressFamilyType
__all__ = (
'APITestCase',
@@ -460,6 +462,8 @@ class APIViewTestCases:
# TODO: Come up with something more elegant
# Temporary hack to support automated testing of reverse generic relations
fields_string += f'{field_name} {{ id }}\n'
elif inspect.isclass(field.type) and issubclass(field.type, IPAddressFamilyType):
fields_string += f'{field_name} {{ value, label }}\n'
else:
fields_string += f'{field_name}\n'

View File

@@ -359,18 +359,18 @@ def prepare_cloned_fields(instance):
return QueryDict(urlencode(params), mutable=True)
def shallow_compare_dict(source_dict, destination_dict, exclude=None):
def shallow_compare_dict(source_dict, destination_dict, exclude=tuple()):
"""
Return a new dictionary of the different keys. The values of `destination_dict` are returned. Only the equality of
the first layer of keys/values is checked. `exclude` is a list or tuple of keys to be ignored.
"""
difference = {}
for key in destination_dict:
if source_dict.get(key) != destination_dict[key]:
if isinstance(exclude, (list, tuple)) and key in exclude:
continue
difference[key] = destination_dict[key]
for key, value in destination_dict.items():
if key in exclude:
continue
if source_dict.get(key) != value:
difference[key] = value
return difference