From ac38be45e0ca3983a124cfc83e4d80d33b42cb81 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Sun, 5 Dec 2021 13:44:36 +0000 Subject: [PATCH] UI: Move forms from backend into UI Signed-off-by: Michael Tremer --- Makefile.am | 10 - src/templates/modules/forms/elements.html | 12 +- src/templates/modules/forms/fieldset.html | 2 - src/templates/modules/forms/index.html | 2 - src/westferry/backend/__init__.py | 2 - src/westferry/backend/forms/__init__.py | 26 --- src/westferry/backend/forms/base.py | 64 ------ src/westferry/backend/forms/choice.py | 29 --- src/westferry/backend/forms/main.py | 178 -------------- src/westferry/backend/forms/text.py | 53 ----- src/westferry/backend/forms/yesno.py | 37 --- src/westferry/ui/forms.py | 268 +++++++++++++++++++++- 12 files changed, 266 insertions(+), 417 deletions(-) delete mode 100644 src/westferry/backend/forms/__init__.py delete mode 100644 src/westferry/backend/forms/base.py delete mode 100644 src/westferry/backend/forms/choice.py delete mode 100644 src/westferry/backend/forms/main.py delete mode 100644 src/westferry/backend/forms/text.py delete mode 100644 src/westferry/backend/forms/yesno.py diff --git a/Makefile.am b/Makefile.am index 57ce56f..53425ce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -106,16 +106,6 @@ westferry_backend_PYTHON = \ westferry_backenddir = $(pythondir)/westferry/backend -westferry_backend_forms_PYTHON = \ - src/westferry/backend/forms/__init__.py \ - src/westferry/backend/forms/base.py \ - src/westferry/backend/forms/choice.py \ - src/westferry/backend/forms/main.py \ - src/westferry/backend/forms/text.py \ - src/westferry/backend/forms/yesno.py - -westferry_backend_formsdir = $(westferry_backenddir)/forms - westferry_handlers_PYTHON = \ src/westferry/handlers/__init__.py \ src/westferry/handlers/analytics.py \ diff --git a/src/templates/modules/forms/elements.html b/src/templates/modules/forms/elements.html index 2fcf996..d6b3c24 100644 --- a/src/templates/modules/forms/elements.html +++ b/src/templates/modules/forms/elements.html @@ -1,15 +1,15 @@ -{% import westferry.backend.forms %} +{% import westferry.ui.forms %} {% for element in elements %} - {% if isinstance(element, westferry.backend.forms.Fieldset) %} + {% if isinstance(element, westferry.ui.forms.Fieldset) %} {% module FormFieldset(element) %} - {% elif isinstance(element, westferry.backend.forms.MultilineTextInput) %} + {% elif isinstance(element, westferry.ui.forms.MultilineTextInput) %} {% module MultilineTextInput(element) %} - {% elif isinstance(element, westferry.backend.forms.PasswordInput) %} + {% elif isinstance(element, westferry.ui.forms.PasswordInput) %} {% module PasswordInput(element) %} - {% elif isinstance(element, westferry.backend.forms.TextInput) %} + {% elif isinstance(element, westferry.ui.forms.TextInput) %} {% module TextInput(element) %} - {% elif isinstance(element, westferry.backend.forms.YesNoInput) %} + {% elif isinstance(element, westferry.ui.forms.YesNoInput) %} {% module YesNoInput(element) %} {% end %} {% end %} diff --git a/src/templates/modules/forms/fieldset.html b/src/templates/modules/forms/fieldset.html index 8ef698d..0ea9469 100644 --- a/src/templates/modules/forms/fieldset.html +++ b/src/templates/modules/forms/fieldset.html @@ -1,5 +1,3 @@ -{% import westferry.backend.forms %} -
{% if fs.label %} {{ fs.label }} diff --git a/src/templates/modules/forms/index.html b/src/templates/modules/forms/index.html index 358f10b..1992ac2 100644 --- a/src/templates/modules/forms/index.html +++ b/src/templates/modules/forms/index.html @@ -1,5 +1,3 @@ -{% import westferry.backend.forms %} -
{% raw xsrf_form_html() %} diff --git a/src/westferry/backend/__init__.py b/src/westferry/backend/__init__.py index 1cc3a3f..cad0eb3 100644 --- a/src/westferry/backend/__init__.py +++ b/src/westferry/backend/__init__.py @@ -20,5 +20,3 @@ ############################################################################### from .main import Backend - -from . import forms diff --git a/src/westferry/backend/forms/__init__.py b/src/westferry/backend/forms/__init__.py deleted file mode 100644 index c9683c6..0000000 --- a/src/westferry/backend/forms/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/python3 -############################################################################### -# # -# Westferry - The IPFire web user interface # -# Copyright (C) 2015 IPFire development team # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -############################################################################### - -from .base import Element -from .main import Form, Fieldset, Group - -from .text import TextInput, MultilineTextInput, PasswordInput -from .yesno import YesNoInput diff --git a/src/westferry/backend/forms/base.py b/src/westferry/backend/forms/base.py deleted file mode 100644 index e2e8633..0000000 --- a/src/westferry/backend/forms/base.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/python3 -############################################################################### -# # -# Westferry - The IPFire web user interface # -# Copyright (C) 2015 IPFire development team # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -############################################################################### - -class Element(object): - # The element type - type = None - - def __init__(self, form, name=None, label=None, default=None, **kwargs): - assert self.type - - self.form = form - self.name = name - self.label = label - self.default = default - - # Is this element editable? - self.disabled = False - self.readonly = False - - # Help text - self.help = None - - # Set defaults for inheriting classes - self._set_defaults() - - # Set args - for k, v in kwargs.items(): - setattr(self, k, v) - - self.init(**kwargs) - - def _set_defaults(self): - pass - - def init(self, *args, **kwargs): - pass - - @property - def value(self): - """ - Returns the value that the user entered for this element. - """ - return self.get_value() - - def get_value(self): - return self.form._get_value(self.name, self.default) diff --git a/src/westferry/backend/forms/choice.py b/src/westferry/backend/forms/choice.py deleted file mode 100644 index 73e2421..0000000 --- a/src/westferry/backend/forms/choice.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/python3 -############################################################################### -# # -# Westferry - The IPFire web user interface # -# Copyright (C) 2015 IPFire development team # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -############################################################################### - -from . import base - -class SingleChoiceInput(base.Element): - type = "single-choice" - - -class MultipleChoiceInput(base.Element): - type = "multiple-choice" diff --git a/src/westferry/backend/forms/main.py b/src/westferry/backend/forms/main.py deleted file mode 100644 index ab49ce0..0000000 --- a/src/westferry/backend/forms/main.py +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/python3 -############################################################################### -# # -# Westferry - The IPFire web user interface # -# Copyright (C) 2015 IPFire development team # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -############################################################################### - -from . import base -from . import choice -from . import text -from . import yesno - -class _Form(object): - def __init__(self, form): - self.form = form - self._elements = [] - - # Is this form disabled? - self.disabled = False - - def __iter__(self): - """ - Iterating over a form will iterate over its elements. - """ - return iter(self._elements) - - @property - def parent_form(self): - if self.form: - return self.form.parent_form - - return self - - @property - def elements(self): - for e in self._elements: - if isinstance(e, _Form): - for i in e._elements: - yield i - - else: - yield e - - def _add_element(self, element): - """ - Adds a new form element to this form. - """ - assert isinstance(element, base.Element) - assert not element in self.elements - - self._elements.append(element) - - def _add_element_class(self, cls, *args, **kwargs): - e = cls(self, *args, **kwargs) - self._add_element(e) - - return e - - def get_element_by_name(self, name): - """ - Returns the element with the given name (if any). - """ - for e in self.elements: - if e.name == name: - return e - - # Have a shortcut to find elements by their name quickly. - get = get_element_by_name - - def add_text_input(self, *args, **kwargs): - """ - Creates a new text input. - """ - return self._add_element_class(text.TextInput, *args, **kwargs) - - def add_multiline_text_input(self, *args, **kwargs): - """ - Creates a new multiline text input. - """ - return self._add_element_class(text.MultilineTextInput, *args, **kwargs) - - def add_password_input(self, *args, **kwargs): - """ - Creates a new password input. - """ - return self._add_element_class(text.PasswordInput, *args, **kwargs) - - def add_yesno_input(self, *args, **kwargs): - """ - Creates a new yes/no input. - """ - return self._add_element_class(yesno.YesNoInput, *args, **kwargs) - - def add_single_choice_input(self, *args, **kwargs): - """ - Creates a new single choice input. - """ - return self._add_element_class(choice.SingleChoiceInput, *args, **kwargs) - - def add_multiple_choice_input(self, *args, **kwargs): - """ - Creates a new multiple choice input. - """ - return self._add_element_class(choice.MultipleChoiceInput, *args, **kwargs) - - def _get_value(self, *args, **kwargs): - return self.parent_form._get_value(*args, **kwargs) - - -class FormGroupMixin(object): - def add_group(self, *args, **kwargs): - group = Group(self, *args, **kwargs) - self._elements.append(group) - - return group - - -class Form(_Form, FormGroupMixin): - def __init__(self, handler, method="POST", action=""): - _Form.__init__(self, None) - self.handler = handler - - self.method = method - self.action = action - - # Text that is shown on the submit button - self.submit_text = None - - def add_fieldset(self, *args, **kwargs): - fs = Fieldset(self, *args, **kwargs) - self._elements.append(fs) - - return fs - - def export(self): - """ - Returns a dictionary with all keys and values - of the form. - """ - ret = {} - - for e in self.elements: - ret[e.name] = e.value - - return ret - - def _get_value(self, name, default=None): - return self.handler.get_argument(name, default) - - -class Fieldset(_Form, FormGroupMixin): - def __init__(self, *args, label=None, help=None, **kwargs): - _Form.__init__(self, *args, **kwargs) - - self.label = label - self.help = help - - -class Group(_Form): - def __init__(self, *args, label=None, **kwargs): - _Form.__init__(self, *args, **kwargs) - - self.label = label - self.help = None diff --git a/src/westferry/backend/forms/text.py b/src/westferry/backend/forms/text.py deleted file mode 100644 index 11b42fa..0000000 --- a/src/westferry/backend/forms/text.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/python3 -############################################################################### -# # -# Westferry - The IPFire web user interface # -# Copyright (C) 2015 IPFire development team # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -############################################################################### - -from . import base - -class TextInput(base.Element): - type = "text" - - def _set_defaults(self): - self.prefix = None - self.suffix = None - - self._placeholder = None - - def get_placeholder(self): - return self._placeholder or self.label - - def set_placeholder(self, placeholder): - self._placeholder = placeholder - - placeholder = property(get_placeholder, set_placeholder) - - -class MultilineTextInput(TextInput): - type = "multiline-text" - - def _set_defaults(self): - TextInput._set_defaults(self) - - # Default number of rows - self.lines = 3 - - -class PasswordInput(TextInput): - type = "password" diff --git a/src/westferry/backend/forms/yesno.py b/src/westferry/backend/forms/yesno.py deleted file mode 100644 index 17b7f44..0000000 --- a/src/westferry/backend/forms/yesno.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/python3 -############################################################################### -# # -# Westferry - The IPFire web user interface # -# Copyright (C) 2015 IPFire development team # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -############################################################################### - -from . import base - -class YesNoInput(base.Element): - type = "yesno" - - def _set_defaults(self): - self.description = None - - def get_value(self): - value = base.Element.get_value(self) - - # This only returns true or false - if value == "on": - return True - - return False diff --git a/src/westferry/ui/forms.py b/src/westferry/ui/forms.py index 76dd616..ec4bfcd 100644 --- a/src/westferry/ui/forms.py +++ b/src/westferry/ui/forms.py @@ -22,8 +22,217 @@ from ..backend import forms from . import base -# Forward-compatibility -from ..backend.forms import Form +class _Form(object): + def __init__(self, form): + self.form = form + self._elements = [] + + # Is this form disabled? + self.disabled = False + + def __iter__(self): + """ + Iterating over a form will iterate over its elements. + """ + return iter(self._elements) + + @property + def parent_form(self): + if self.form: + return self.form.parent_form + + return self + + @property + def elements(self): + for e in self._elements: + if isinstance(e, _Form): + for i in e._elements: + yield i + + else: + yield e + + def _add_element(self, element): + """ + Adds a new form element to this form. + """ + assert isinstance(element, Element) + assert not element in self.elements + + self._elements.append(element) + + def _add_element_class(self, cls, *args, **kwargs): + e = cls(self, *args, **kwargs) + self._add_element(e) + + return e + + def get_element_by_name(self, name): + """ + Returns the element with the given name (if any). + """ + for e in self.elements: + if e.name == name: + return e + + # Have a shortcut to find elements by their name quickly. + get = get_element_by_name + + def add_text_input(self, *args, **kwargs): + """ + Creates a new text input. + """ + return self._add_element_class(TextInput, *args, **kwargs) + + def add_multiline_text_input(self, *args, **kwargs): + """ + Creates a new multiline text input. + """ + return self._add_element_class(MultilineTextInput, *args, **kwargs) + + def add_password_input(self, *args, **kwargs): + """ + Creates a new password input. + """ + return self._add_element_class(PasswordInput, *args, **kwargs) + + def add_yesno_input(self, *args, **kwargs): + """ + Creates a new yes/no input. + """ + return self._add_element_class(YesNoInput, *args, **kwargs) + + def add_single_choice_input(self, *args, **kwargs): + """ + Creates a new single choice input. + """ + return self._add_element_class(SingleChoiceInput, *args, **kwargs) + + def add_multiple_choice_input(self, *args, **kwargs): + """ + Creates a new multiple choice input. + """ + return self._add_element_class(MultipleChoiceInput, *args, **kwargs) + + def _get_value(self, *args, **kwargs): + return self.parent_form._get_value(*args, **kwargs) + + +class FormGroupMixin(object): + def add_group(self, *args, **kwargs): + group = Group(self, *args, **kwargs) + self._elements.append(group) + + return group + + +class Form(_Form, FormGroupMixin): + def __init__(self, handler, method="POST", action=""): + _Form.__init__(self, None) + self.handler = handler + + self.method = method + self.action = action + + # Text that is shown on the submit button + self.submit_text = None + + def add_fieldset(self, *args, **kwargs): + fs = Fieldset(self, *args, **kwargs) + self._elements.append(fs) + + return fs + + def export(self): + """ + Returns a dictionary with all keys and values + of the form. + """ + ret = {} + + for e in self.elements: + ret[e.name] = e.value + + return ret + + def _get_value(self, name, default=None): + return self.handler.get_argument(name, default) + + +class Fieldset(_Form, FormGroupMixin): + def __init__(self, *args, label=None, help=None, **kwargs): + _Form.__init__(self, *args, **kwargs) + + self.label = label + self.help = help + + +class FormFieldsetModule(base.BaseUIModule): + def render(self, fieldset): + return self.render_string("modules/forms/fieldset.html", fs=fieldset) + + +class Group(_Form): + def __init__(self, *args, label=None, **kwargs): + _Form.__init__(self, *args, **kwargs) + + self.label = label + self.help = None + + +class Element(object): + # The element type + type = None + + def __init__(self, form, name=None, label=None, default=None, **kwargs): + assert self.type + + self.form = form + self.name = name + self.label = label + self.default = default + + # Is this element editable? + self.disabled = False + self.readonly = False + + # Help text + self.help = None + + # Set defaults for inheriting classes + self._set_defaults() + + # Set args + for k, v in kwargs.items(): + setattr(self, k, v) + + self.init(**kwargs) + + def _set_defaults(self): + pass + + def init(self, *args, **kwargs): + pass + + @property + def value(self): + """ + Returns the value that the user entered for this element. + """ + return self.get_value() + + def get_value(self): + return self.form._get_value(self.name, self.default) + + +class SingleChoiceInput(Element): + type = "single-choice" + + +class MultipleChoiceInput(Element): + type = "multiple-choice" + class FormModule(base.BaseUIModule): def render(self, form): @@ -36,27 +245,70 @@ class FormElementsModule(base.BaseUIModule): return self.render_string("modules/forms/elements.html", elements=elements) -class FormFieldsetModule(base.BaseUIModule): - def render(self, fieldset): - return self.render_string("modules/forms/fieldset.html", fs=fieldset) +class TextInput(Element): + type = "text" + + def _set_defaults(self): + self.prefix = None + self.suffix = None + + self._placeholder = None + + def get_placeholder(self): + return self._placeholder or self.label + + def set_placeholder(self, placeholder): + self._placeholder = placeholder + + placeholder = property(get_placeholder, set_placeholder) class TextInputModule(base.BaseUIModule): def render(self, input): - assert isinstance(input, forms.TextInput) + assert isinstance(input, TextInput) return self.render_string("modules/forms/inputs/text.html", input=input) +class PasswordInput(TextInput): + type = "password" + + +class MultilineTextInput(TextInput): + type = "multiline-text" + + def _set_defaults(self): + TextInput._set_defaults(self) + + # Default number of rows + self.lines = 3 + + class MultilineTextInputModule(base.BaseUIModule): def render(self, input): - assert isinstance(input, forms.MultilineTextInput) + assert isinstance(input, MultilineTextInput) return self.render_string("modules/forms/inputs/textarea.html", input=input) +class YesNoInput(Element): + type = "yesno" + + def _set_defaults(self): + self.description = None + + def get_value(self): + value = Element.get_value(self) + + # This only returns true or false + if value == "on": + return True + + return False + + class YesNoInputModule(base.BaseUIModule): def render(self, input): - assert isinstance(input, forms.YesNoInput) + assert isinstance(input, YesNoInput) return self.render_string("modules/forms/inputs/checkbox.html", input=input) -- 2.47.3