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 \
src/westferry/handlers/base.py \
+ src/westferry/handlers/demo.py \
src/westferry/handlers/index.py
westferry_handlersdir = $(pythondir)/westferry/handlers
westferry_ui_PYTHON = \
src/westferry/ui/__init__.py \
src/westferry/ui/base.py \
+ src/westferry/ui/forms.py \
src/westferry/ui/graphs.py \
src/westferry/ui/menu.py \
src/westferry/ui/utils.py
templatesdir = $(datadir)/westferry/templates
dist_templates_DATA = \
- src/templates/base.html
+ src/templates/base.html \
+ src/templates/demo.html
templates_modulesdir = $(templatesdir)/modules
dist_templates_modules_DATA =
+templates_modules_formsdir = $(templates_modulesdir)/forms
+
+dist_templates_modules_forms_DATA = \
+ src/templates/modules/forms/element.html \
+ src/templates/modules/forms/fieldset.html \
+ src/templates/modules/forms/group.html \
+ src/templates/modules/forms/index.html
+
+templates_modules_forms_inputsdir = $(templates_modules_formsdir)/inputs
+
+dist_templates_modules_forms_inputs_DATA = \
+ src/templates/modules/forms/inputs/base.html \
+ src/templates/modules/forms/inputs/checkbox.html \
+ src/templates/modules/forms/inputs/text.html \
+ src/templates/modules/forms/inputs/textarea.html
+
templates_modules_graphsdir = $(templates_modulesdir)/graphs
dist_templates_modules_graphs_DATA = \
src/westferry/backend/__version__.py
src/westferry/handlers/analytics.py
src/westferry/handlers/base.py
+src/westferry/handlers/demo.py
+src/westferry/handlers/index.py
src/westferry/__init__.py
--- /dev/null
+{% extends "base.html" %}
+
+{% block main %}
+ {% module Form(form) %}
+{% end block %}
--- /dev/null
+<div class="form-group">
+ {% if e.label %}
+ <label class="col-sm-2 control-label">{{ e.label }}</label>
+ {% end %}
+
+ <div class="col-sm-10 {% if not e.label %}col-sm-offset-2{% end %}">
+ {% module FormInput(e) %}
+
+ {% if e.help %}
+ <span class="help-block">{{ e.help }}</span>
+ {% end %}
+ </div>
+</div>
--- /dev/null
+{% import westferry.backend.forms %}
+
+<fieldset {% if fs.disabled %}disabled{% end %}>
+ {% if fs.label %}
+ <legend>{{ fs.label }}</legend>
+ {% else %}
+ <hr>
+ {% end %}
+
+ {% if fs.help %}
+ <div class="row">
+ <div class="col-md-10 col-md-offset-2">
+ <span class="help-block">{{ fs.help }}</span>
+ </div>
+ </div>
+ {% end %}
+
+ {% for e in fs %}
+ {% if isinstance(e, westferry.backend.forms.Group) %}
+ {% module FormGroup(e) %}
+ {% else %}
+ {% module FormElement(e) %}
+ {% end %}
+ {% end %}
+</fieldset>
--- /dev/null
+<div class="form-inline">
+ {% for e in group %}
+ <div class="form-group">
+ {% if e.label %}
+ <label>{{ e.label }}</label>
+ {% end %}
+
+ {% module FormInput(e) %}
+ </div>
+ {% end %}
+</div>
--- /dev/null
+{% import westferry.backend.forms %}
+
+<form class="form-horizontal" method="{{ f.method }}" action="{{ f.action }}">
+ {% raw xsrf_form_html() %}
+
+ {% for e in f %}
+ {% if isinstance(e, westferry.backend.forms.Fieldset) %}
+ {% module FormFieldset(e) %}
+ {% elif isinstance(e, westferry.backend.forms.Group) %}
+ {% module FormGroup(e) %}
+ {% else %}
+ {% module FormElement(e) %}
+ {% end %}
+ {% end %}
+
+ <div class="form-group">
+ <div class="col-sm-10 col-sm-offset-2">
+ <button type="submit" class="btn btn-primary">{{ f.submit_text or _("Submit") }}</button>
+ </div>
+ </div>
+</form>
--- /dev/null
+{% import westferry.backend.forms %}
+
+{% if isinstance(input, westferry.backend.forms.MultilineTextInput) %}
+ {% module MultilineTextInput(input) %}
+{% elif isinstance(input, westferry.backend.forms.PasswordInput) %}
+ {% module PasswordInput(input) %}
+{% elif isinstance(input, westferry.backend.forms.TextInput) %}
+ {% module TextInput(input) %}
+{% elif isinstance(input, westferry.backend.forms.YesNoInput) %}
+ {% module YesNoInput(input) %}
+{% end %}
--- /dev/null
+<div class="checkbox">
+ <label>
+ <input type="checkbox" name="{{ input.name }}" {% if input.value %}checked{% end %}>
+ {% if input.description %}{{ input.description }}{% end %}
+ </label>
+</div>
--- /dev/null
+{% if input.prefix or input.suffix %}
+ <div class="input-group">
+ {% if input.prefix %}
+ <div class="input-group-addon">{{ input.prefix }}</div>
+ {% end%}
+{% end %}
+
+<input type="{{ input.type }}" name="{{ input.name }}" class="form-control"
+ {% if input.placeholder %}placeholder="{{ input.placeholder }}"{% end %}
+ {% if input.disabled %}disabled{% end %}
+ {% if input.readonly %}readonly{% end %} />
+
+{% if input.prefix or input.suffix %}
+ {% if input.suffix %}
+ <div class="input-group-addon">{{ input.suffix }}</div>
+ {% end %}
+ </div>
+{% end %}
--- /dev/null
+<textarea class="form-control" name="{{ input.name }}" rows="{{ input.lines }}"
+ {% if input.placeholder %}placeholder="{{ input.placeholder }}"{% end %}
+ >{% if input.value %}{{ input.value }}{% end %}</textarea>
###############################################################################
from .main import Backend
+
+from . import forms
--- /dev/null
+#!/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 <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+from .base import Element
+from .main import Form, Fieldset, Group
+
+from .text import TextInput, MultilineTextInput, PasswordInput
+from .yesno import YesNoInput
--- /dev/null
+#!/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 <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+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)
--- /dev/null
+#!/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 <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+from . import base
+
+class SingleChoiceInput(base.Element):
+ type = "single-choice"
+
+
+class MultipleChoiceInput(base.Element):
+ type = "multiple-choice"
--- /dev/null
+#!/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 <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+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
--- /dev/null
+#!/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 <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+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"
--- /dev/null
+#!/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 <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+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
from . import base
from . import analytics
+from . import demo
from . import index
def get_handlers():
--- /dev/null
+#!/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 <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+from . import base
+from .. import backend
+from .. import ui
+
+from ..i18n import N_, _
+
+class DemoBaseHandler(base.BaseHandler):
+ @property
+ def menu(self):
+ _ = self.locale.translate
+
+ m = ui.menu.Menu(self, _("Demo"))
+
+ # Overview
+ m.add_handler(DemoOverviewHandler)
+ m.add_divider()
+
+ # Forms
+ m.add_handler(DemoFormHandler)
+
+ return m
+
+
+class DemoOverviewHandler(DemoBaseHandler):
+ url = r"/demo"
+ title = N_("Overview")
+
+ def get(self):
+ self.render("base.html")
+
+
+class DemoFormHandler(DemoBaseHandler):
+ url = r"/demo/forms"
+ title = N_("Forms")
+
+ def get(self):
+ self.render("demo.html", form=self.form)
+
+ def post(self):
+ data = self.form.export()
+ self.finish(data)
+
+ @property
+ def form(self):
+ """
+ This creates a simple form that asks the user
+ for their address.
+ """
+ _ = self.locale.translate
+
+ form = backend.forms.Form(self)
+ form.submit_text = _("Order")
+
+ # First name
+ e = form.add_text_input("first_name", label=_("First Name"), placeholder="John")
+ e.help = _("Please enter your first name")
+
+ # Last name
+ e = form.add_text_input("last_name", label=_("Last Name"), placeholder="Doe")
+ e.help = _("Please enter your last name")
+
+ # We create a fieldset for the address
+ fs1 = form.add_fieldset(label=_("Address"))
+ fs1.help = _("Please enter your address")
+
+ e = fs1.add_text_input("street", label=_("Street"))
+ e = fs1.add_text_input("city", label=_("City"))
+ e = fs1.add_text_input("post_code", label=_("Postcode"))
+
+ # Fieldset without label
+ fs2 = form.add_fieldset()
+
+ # Donation amount
+ e = fs2.add_text_input("donation", label=_("Donation"),
+ placeholder=_("Donation"), suffix="€")
+
+ # Notes
+ e = fs2.add_multiline_text_input("notes", label=_("Notes"))
+
+ # Subscribe to newsletter?
+ e = fs2.add_yesno_input("subscribe_to_newsletter", description=_("Subscribe to newsletter?"))
+
+ return form
###############################################################################
from . import base
+from . import forms
from . import graphs
from . import menu
from . import utils
--- /dev/null
+#!/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 <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+from ..backend import forms
+from . import base
+
+class FormModule(base.BaseUIModule):
+ def render(self, form):
+ return self.render_string("modules/forms/index.html",
+ f=form, forms=forms)
+
+
+class FormElementModule(base.BaseUIModule):
+ def render(self, element):
+ assert isinstance(element, forms.Element)
+
+ return self.render_string("modules/forms/element.html", e=element)
+
+
+class FormFieldsetModule(base.BaseUIModule):
+ def render(self, fieldset):
+ return self.render_string("modules/forms/fieldset.html", fs=fieldset)
+
+
+class FormGroupModule(base.BaseUIModule):
+ def render(self, group):
+ return self.render_string("modules/forms/group.html", group=group)
+
+
+class FormInputModule(base.BaseUIModule):
+ """
+ This is a proxy module that will pick the right
+ *InputModule.
+ """
+ def render(self, input):
+ return self.render_string("modules/forms/inputs/base.html", input=input)
+
+
+class TextInputModule(base.BaseUIModule):
+ def render(self, input):
+ assert isinstance(input, forms.TextInput)
+
+ return self.render_string("modules/forms/inputs/text.html", input=input)
+
+
+class MultilineTextInputModule(base.BaseUIModule):
+ def render(self, input):
+ assert isinstance(input, forms.MultilineTextInput)
+
+ return self.render_string("modules/forms/inputs/textarea.html", input=input)
+
+
+class YesNoInputModule(base.BaseUIModule):
+ def render(self, input):
+ assert isinstance(input, forms.YesNoInput)
+
+ return self.render_string("modules/forms/inputs/checkbox.html", input=input)