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

Introduce UserConfigForm for managing user preferences

This commit is contained in:
jeremystretch
2021-12-21 16:29:01 -05:00
parent 5e32c69e0e
commit 70f257b1ea
8 changed files with 103 additions and 105 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,6 @@
import { initConnectionToggle } from './connectionToggle';
import { initDepthToggle } from './depthToggle';
import { initMoveButtons } from './moveOptions';
import { initPreferenceUpdate } from './preferences';
import { initReslug } from './reslug';
import { initSelectAll } from './selectAll';
@@ -11,7 +10,6 @@ export function initButtons(): void {
initConnectionToggle,
initReslug,
initSelectAll,
initPreferenceUpdate,
initMoveButtons,
]) {
func();

View File

@@ -1,30 +0,0 @@
import { setColorMode } from '../colorMode';
import { getElement } from '../util';
/**
* Perform actions in the UI based on the value of user profile updates.
*
* @param event Form Submit
*/
function handlePreferenceSave(event: Event): void {
// Create a FormData instance to access the form values.
const form = event.currentTarget as HTMLFormElement;
const formData = new FormData(form);
// Update the UI color mode immediately when the user preference changes.
if (formData.get('ui.colormode') === 'dark') {
setColorMode('dark');
} else if (formData.get('ui.colormode') === 'light') {
setColorMode('light');
}
}
/**
* Initialize handlers for user profile updates.
*/
export function initPreferenceUpdate(): void {
const form = getElement<HTMLFormElement>('preferences-update');
if (form !== null) {
form.addEventListener('submit', handlePreferenceSave);
}
}

View File

@@ -1,57 +1,27 @@
{% extends 'users/base.html' %}
{% load helpers %}
{% load form_helpers %}
{% block title %}User Preferences{% endblock %}
{% block content %}
<form method="post" action="" id="preferences-update">
{% csrf_token %}
<div class="field-group mb-3">
<h5>Color Mode</h5>
<p class="text-muted">Set preferred UI color mode</p>
{% with color_mode=preferences|get_key:'ui.colormode'%}
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="ui.colormode" id="color-mode-preference-dark" value="dark"{% if color_mode == 'dark'%} checked{% endif %}>
<label class="form-check-label" for="color-mode-preference-dark">Dark</label>
{% for group, fields in form.Meta.fieldsets %}
<div class="field-group my-5">
<div class="row mb-2">
<h5 class="offset-sm-3">{{ group }}</h5>
</div>
{% for name in fields %}
{% render_field form|getfield:name %}
{% endfor %}
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="ui.colormode" id="color-mode-preference-light" value="light"{% if color_mode == 'light'%} checked{% endif %}>
<label class="form-check-label" for="color-mode-preference-light">Light</label>
</div>
{% endwith %}
{% endfor %}
<div class="text-end my-3">
<a class="btn btn-outline-secondary" href="{% url 'user:preferences' %}">Cancel</a>
<button type="submit" name="_update" class="btn btn-primary">Save </button>
</div>
<div class="row mb-3">
<div class="col">
<button type="submit" class="btn btn-primary" name="_update">Save</button>
</div>
</div>
{% if preferences %}
<div class="field-group mb-3">
<h5>Other Preferences</h5>
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" class="toggle form-check-input" title="Toggle All"></th>
<th>Preference</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for key, value in preferences.items %}
<tr>
<td class="min-width"><input class="form-check-input" type="checkbox" name="pk" value="{{ key }}"></td>
<td><samp>{{ key }}</samp></td>
<td><samp>{{ value }}</samp></td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit" class="btn btn-danger" name="_delete">
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Clear Selected
</button>
</div>
{% else %}
<h3 class="text-muted text-center">No preferences found</h3>
{% endif %}
</form>
{% endblock %}

View File

@@ -2,7 +2,9 @@ from django import forms
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm
from utilities.forms import BootstrapMixin, DateTimePicker
from .models import Token
from utilities.paginator import EnhancedPaginator
from utilities.utils import flatten_dict
from .models import Token, UserConfig
class LoginForm(BootstrapMixin, AuthenticationForm):
@@ -13,6 +15,68 @@ class PasswordChangeForm(BootstrapMixin, DjangoPasswordChangeForm):
pass
def get_page_lengths():
return [
(v, str(v)) for v in EnhancedPaginator.default_page_lengths
]
class UserConfigForm(BootstrapMixin, forms.ModelForm):
pagination__per_page = forms.TypedChoiceField(
label='Page length',
coerce=lambda val: int(val),
choices=get_page_lengths,
required=False
)
ui__colormode = forms.ChoiceField(
label='Color mode',
choices=(
('light', 'Light'),
('dark', 'Dark'),
),
required=False
)
extras__configcontext__format = forms.ChoiceField(
label='ConfigContext format',
choices=(
('json', 'JSON'),
('yaml', 'YAML'),
),
required=False
)
class Meta:
model = UserConfig
fields = ()
fieldsets = (
('User Interface', (
'pagination__per_page',
'ui__colormode',
)),
('Miscellaneous', (
'extras__configcontext__format',
)),
)
def __init__(self, *args, instance=None, **kwargs):
# Get initial data from UserConfig instance
initial_data = flatten_dict(instance.data, separator='__')
kwargs['initial'] = initial_data
super().__init__(*args, instance=instance, **kwargs)
def save(self, *args, **kwargs):
# Set UserConfig data
for field_name, value in self.cleaned_data.items():
pref_name = field_name.replace('__', '.')
print(f'{pref_name}: {value}')
self.instance.set(pref_name, value, commit=False)
return super().save(*args, **kwargs)
class TokenForm(BootstrapMixin, forms.ModelForm):
key = forms.CharField(
required=False,

View File

@@ -19,7 +19,7 @@ from extras.models import ObjectChange
from extras.tables import ObjectChangeTable
from netbox.config import get_config
from utilities.forms import ConfirmationForm
from .forms import LoginForm, PasswordChangeForm, TokenForm
from .forms import LoginForm, PasswordChangeForm, TokenForm, UserConfigForm
from .models import Token
@@ -137,32 +137,28 @@ class UserConfigView(LoginRequiredMixin, View):
template_name = 'users/preferences.html'
def get(self, request):
userconfig = request.user.config
form = UserConfigForm(instance=userconfig)
return render(request, self.template_name, {
'preferences': request.user.config.all(),
'form': form,
'active_tab': 'preferences',
})
def post(self, request):
userconfig = request.user.config
data = userconfig.all()
form = UserConfigForm(request.POST, instance=userconfig)
# Delete selected preferences
if "_delete" in request.POST:
for key in request.POST.getlist('pk'):
if key in data:
userconfig.clear(key)
# Update specific values
elif "_update" in request.POST:
for key in request.POST:
if not key.startswith('_') and not key.startswith('csrf'):
for value in request.POST.getlist(key):
userconfig.set(key, value)
if form.is_valid():
form.save()
userconfig.save()
messages.success(request, "Your preferences have been updated.")
messages.success(request, "Your preferences have been updated.")
return redirect('user:preferences')
return redirect('user:preferences')
return render(request, self.template_name, {
'form': form,
'active_tab': 'preferences',
})
class ChangePasswordView(LoginRequiredMixin, View):

View File

@@ -282,7 +282,7 @@ def flatten_dict(d, prefix='', separator='.'):
for k, v in d.items():
key = separator.join([prefix, k]) if prefix else k
if type(v) is dict:
ret.update(flatten_dict(v, prefix=key))
ret.update(flatten_dict(v, prefix=key, separator=separator))
else:
ret[key] = v
return ret