openwebrx-clone/owrx/controllers/settings/__init__.py

188 lines
5.5 KiB
Python
Raw Normal View History

from owrx.config import Config
from owrx.controllers.admin import AuthorizationMixin
from owrx.controllers.template import WebpageController
from owrx.form.error import FormError
from owrx.breadcrumb import Breadcrumb, BreadcrumbItem, BreadcrumbMixin
2021-02-15 14:40:37 +00:00
from abc import ABCMeta, abstractmethod
from urllib.parse import parse_qs
2021-04-27 16:23:59 +00:00
import logging
logger = logging.getLogger(__name__)
class Section(object):
def __init__(self, title, *inputs):
self.title = title
self.inputs = inputs
def render_input(self, input, data, errors):
return input.render(data, errors)
2021-02-22 22:49:28 +00:00
def render_inputs(self, data, errors):
return "".join([self.render_input(i, data, errors) for i in self.inputs])
2021-02-22 22:49:28 +00:00
def classes(self):
return ["col-12", "settings-section"]
def render(self, data, errors):
return """
2021-02-22 22:49:28 +00:00
<div class="{classes}">
<h3 class="settings-header">
{title}
</h3>
{inputs}
</div>
""".format(
classes=" ".join(self.classes()), title=self.title, inputs=self.render_inputs(data, errors)
)
def parse(self, data):
parsed_data = {}
errors = []
for i in self.inputs:
try:
parsed_data.update(i.parse(data))
except FormError as e:
errors.append(e)
except Exception as e:
errors.append(FormError(i.id, "{}: {}".format(type(e).__name__, e)))
return parsed_data, errors
class SettingsController(AuthorizationMixin, WebpageController):
def indexAction(self):
self.serve_template("settings.html", **self.template_variables())
2021-02-15 14:40:37 +00:00
class SettingsFormController(AuthorizationMixin, BreadcrumbMixin, WebpageController, metaclass=ABCMeta):
def __init__(self, handler, request, options):
super().__init__(handler, request, options)
self.errors = {}
2021-04-27 16:23:59 +00:00
self.globalError = None
2021-02-15 14:40:37 +00:00
@abstractmethod
def getSections(self):
pass
@abstractmethod
def getTitle(self):
pass
2021-02-19 17:18:25 +00:00
def getData(self):
return Config.get()
def getErrors(self):
return self.errors
2021-02-15 14:40:37 +00:00
def render_sections(self):
sections = "".join(section.render(self.getData(), self.getErrors()) for section in self.getSections())
buttons = self.render_buttons()
2021-02-15 14:40:37 +00:00
return """
<form class="settings-body" method="POST">
{sections}
2021-02-24 22:04:23 +00:00
<div class="buttons container">
{buttons}
2021-02-15 14:40:37 +00:00
</div>
</form>
""".format(
sections=sections,
buttons=buttons,
2021-02-15 14:40:37 +00:00
)
def render_buttons(self):
return """
<button type="submit" class="btn btn-primary">Apply and save</button>
"""
2021-02-15 14:40:37 +00:00
def indexAction(self):
self.serve_template("settings/general.html", **self.template_variables())
def template_variables(self):
variables = super().template_variables()
2021-02-18 22:05:43 +00:00
variables["content"] = self.render_sections()
2021-02-15 14:40:37 +00:00
variables["title"] = self.getTitle()
variables["modal"] = self.buildModal()
2021-04-27 16:23:59 +00:00
variables["error"] = self.renderGlobalError()
2021-02-15 14:40:37 +00:00
return variables
def parseFormData(self):
data = parse_qs(self.get_body().decode("utf-8"), keep_blank_values=True)
result = {}
errors = []
for section in self.getSections():
section_data, section_errors = section.parse(data)
result.update(section_data)
errors += section_errors
return result, errors
2021-02-15 14:40:37 +00:00
def getSuccessfulRedirect(self):
return self.get_document_root() + self.request.path[1:]
def _mergeErrors(self, errors):
result = {}
for e in errors:
if e.getKey() not in result:
result[e.getKey()] = []
result[e.getKey()].append(e.getMessage())
return result
2021-02-15 14:40:37 +00:00
def processFormData(self):
2021-04-27 16:23:59 +00:00
data = None
errors = None
try:
data, errors = self.parseFormData()
except Exception as e:
logger.exception("Error while parsing form data")
self.globalError = str(e)
return self.indexAction()
if errors:
self.errors = self._mergeErrors(errors)
2021-04-27 16:23:59 +00:00
return self.indexAction()
try:
self.processData(data)
self.store()
self.send_redirect(self.getSuccessfulRedirect())
2021-04-27 16:23:59 +00:00
except Exception as e:
logger.exception("Error while processing form data")
self.globalError = str(e)
return self.indexAction()
2021-02-15 14:40:37 +00:00
def processData(self, data):
2021-02-19 17:45:29 +00:00
config = self.getData()
2021-02-15 14:40:37 +00:00
for k, v in data.items():
if v is None:
if k in config:
del config[k]
else:
config[k] = v
2021-02-19 17:45:29 +00:00
def store(self):
Config.get().store()
def buildModal(self):
return ""
2021-04-27 16:23:59 +00:00
def renderGlobalError(self):
if self.globalError is None:
return ""
return """
<div class="card text-white bg-danger">
<div class="card-header">Error</div>
<div class="card-body">
<div>Your settings could not be saved due to an error:</div>
<div>{error}</div>
</div>
</div>
""".format(
error=self.globalError
)
class SettingsBreadcrumb(Breadcrumb):
def __init__(self):
super().__init__([])
self.append(BreadcrumbItem("Settings", "settings"))