mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
The Settings page saved whatever was posted, and some lists offered values the panel cannot use: - Quota Debug could not be turned off: "False" is a non-empty text and counted as on. - VM Clone Name Prefix showed as a True/False list, so no prefix could be typed, and touching it set the prefix to True. - Disk SCSI Controller listed " lsilogic" with a space, never shown as chosen, and virtio-blk, which is no SCSI controller. - VM Machine Type listed x86_64, an architecture; VM CD-ROM Device listed virtio, a bus a CD-ROM cannot use. - Logs per Page and Disk Owner UID/GID are read with int(): a letter broke the logs page and VM create. A migration fixes the lists and moves a value they no longer offer to the default (the clone prefix keeps its text). A listed setting now takes one of its choices, and every number setting a whole number in its range, checked before it is saved.
96 lines
3.9 KiB
Python
96 lines
3.9 KiB
Python
from pathlib import Path
|
|
|
|
from admin.decorators import superuser_only
|
|
from django.conf import settings
|
|
from django.contrib import messages
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import render
|
|
from django.utils.translation import gettext_noop as _
|
|
from logs.audit import audit
|
|
from operations.utils import BOUNDS
|
|
|
|
from appsettings.models import AppSettings
|
|
|
|
|
|
# the themes "manage.py build_themes" compiles; base.html links the chosen one
|
|
THEMES_DIR = Path(settings.BASE_DIR, "static", "css", "themes")
|
|
|
|
# the whole-number settings and their range: they are read as numbers, and a
|
|
# letter would break the page or the job that reads one
|
|
NUMBERS = {
|
|
**{key: bounds[:2] for key, bounds in BOUNDS.items()},
|
|
"LOGS_PER_PAGE": (1, 1000),
|
|
"LOGIN_FAILURE_LIMIT": (1, 1000),
|
|
"LOGIN_LOCKOUT_MINUTES": (1, 10080),
|
|
"SNAPSHOT_FREE_SPACE_PERCENT": (0, 100),
|
|
"SNAPSHOT_FREE_SPACE_MIN_GIB": (0, 100000),
|
|
"INSTANCE_VOLUME_DEFAULT_OWNER_UID": (0, 4294967294),
|
|
"INSTANCE_VOLUME_DEFAULT_OWNER_GID": (0, 4294967294),
|
|
}
|
|
|
|
|
|
def refusal(setting, value):
|
|
"""Why the value cannot be saved for the setting, or None."""
|
|
if setting.key in NUMBERS:
|
|
low, high = NUMBERS[setting.key]
|
|
if not (value.isascii() and value.isdigit() and len(value) <= 10 and low <= int(value) <= high):
|
|
return _("%(setting)s must be a whole number between %(low)s and %(high)s") % {
|
|
"setting": setting.name, "low": low, "high": high}
|
|
elif setting.choices and value not in setting.choices_as_list():
|
|
return _("%(setting)s must be one of %(choices)s") % {
|
|
"setting": setting.name, "choices": ", ".join(setting.choices_as_list())}
|
|
return None
|
|
|
|
|
|
@superuser_only
|
|
def appsettings(request):
|
|
"""
|
|
:param request:
|
|
:return:
|
|
"""
|
|
bootstrap_theme = AppSettings.objects.get(key="BOOTSTRAP_THEME")
|
|
themes_list = sorted(p.name.removesuffix(".min.css") for p in THEMES_DIR.glob("*.min.css"))
|
|
|
|
# the theme has its own form; the other settings are listed one by one
|
|
appsettings = AppSettings.objects.exclude(
|
|
description__startswith="Bootstrap"
|
|
).order_by("name")
|
|
|
|
if request.method == "POST":
|
|
if "BOOTSTRAP_THEME" in request.POST:
|
|
theme = request.POST.get("BOOTSTRAP_THEME", "")
|
|
if theme not in themes_list:
|
|
messages.error(request, _("Unknown theme: %(theme)s") % {"theme": theme})
|
|
return HttpResponseRedirect(request.get_full_path())
|
|
if theme != bootstrap_theme.value:
|
|
with audit(request, None, "Setting Theme", f"change to {theme}"):
|
|
bootstrap_theme.value = theme
|
|
bootstrap_theme.save()
|
|
msg = _("Theme is changed. Now: %(theme)s") % {"theme": theme}
|
|
messages.success(request, msg)
|
|
return HttpResponseRedirect(request.get_full_path())
|
|
|
|
for setting in appsettings:
|
|
if setting.key in request.POST:
|
|
value = request.POST.get(setting.key, "")
|
|
refused = refusal(setting, value)
|
|
if refused:
|
|
messages.error(request, refused)
|
|
return HttpResponseRedirect(request.get_full_path())
|
|
try:
|
|
if value != setting.value:
|
|
with audit(request, None, f"Setting {setting.name}", f"change to {value}"):
|
|
setting.value = value
|
|
setting.save()
|
|
|
|
msg = _("%(setting)s is changed. Now: %(value)s") % {
|
|
"setting": setting.name,
|
|
"value": setting.value,
|
|
}
|
|
messages.success(request, msg)
|
|
except Exception as err:
|
|
msg = err
|
|
messages.error(request, msg)
|
|
return HttpResponseRedirect(request.get_full_path())
|
|
|
|
return render(request, "appsettings.html", locals())
|