class ForisAjaxForm(object):
class ForisFormElement(object):
class ForisForm(ForisFormElement):
class Section(ForisFormElement):
class Field(ForisFormElement):
    def make_form(self, data: typing.Optional[dict]):
    def convert_data_from_form_to_backend(self, *args, **kwargs):
    def convert_data_from_backend_to_form(self, *args, **kwargs):
    def __init__(self, data=None, controller_id=None):
    def __init__(self, name):
    def __iter__(self):
    def _add(self, child):
    def _remove(self, child):
    def sections(self):
    def __init__(self, name, data=None, validators=[]):
    def data(self):
    def current_data(self):
    def clean_data(self, data):
    def invalidate_data(self):
    def _form(self):
    def valid(self):
    def _get_all_fields(self, element=None, fields=None):
    def get_active_fields(self, element=None, data=None):
    def add_section(self, *args, **kwargs):
    def active_fields(self):
    def errors(self):
    def render_errors(self):
    def render(self):
    def save(self):
    def validate(self):
    def add_callback(self, cb):
    def process_callbacks(self, form_data):
    def __init__(self, main_form, name, title, description=None):
    def active_fields(self):
    def add_field(self, *args, **kwargs):
    def add_section(self, *args, **kwargs):
    def render(self):
    def __init__(
        self,
        main_form,
        type,
        name,
        label=None,
        required=False,
        preproc=None,
        validators=None,
        hint="",
        multifield=False,
        **kwargs
    ):
    def __str__(self):
    def _generate_html_classes(self):
    def _generate_html_data(self):
    def field(self):
    def html_id(self):
    def label_tag(self):
        def create_label(text):
    def errors(self):
    def hidden(self):
    def render(self):
    def requires(self, field, value=None):
    def has_requirements(self, data): class Validator(object):
class RegExp(Validator):
class NotEmpty(Validator):
class IPv4(Validator):
class IPv4Netmask(Validator):
class IPv6(Validator):
class AnyIP(Validator):
class IPv6Prefix(Validator):
class IPv4Prefix(Validator):
class PositiveInteger(Validator):
class Time(RegExp):
class Datetime(Validator):
class FloatRange(Validator):
class Domain(Validator):
class MacAddress(Validator):
class Duid(Validator):
class InRange(Validator):
class LenRange(Validator):
class ByteLenRange(Validator):
class EqualTo(Validator):
class RequiredWithOtherFields(Validator):
class DhcpRangeValidator:
class DhcpRangeRouterIpValidator:
    def __deepcopy__(self, memo):
    def __init__(self, msg):
    def valid(self, value):
def convert_to_anchored_pattern(pattern):
    def __init__(self, msg, reg_exp):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self):
    def __init__(self):
    def valid(self, value):
    def __init__(self, low, high):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self):
    def valid(self, value):
    def __init__(self, low, high):
    def valid(self, value):
    def __init__(self, low, high):
    def valid(self, value):
    def __init__(self, low, high):
    def valid(self, value):
    def __init__(self, field1, field2, message):
    def valid(self, data):
    def __init__(self, fields, message):
    def valid(self, data):
    def __init__(self, netmask_field, dhcp_start_field, dhcp_limit_field, msg, skip_conditions=[]):
    def valid(self, data):
    def __init__(
        self,
        router_ip_field,
        netmask_field,
        dhcp_start_field,
        dhcp_limit_field,
        msg,
        skip_conditions=[],
    ):
    def valid(self, data):
def validators_as_data_dict(validators): class Checkbox(Input):
    """Checkbox input.

    >>> Checkbox('foo', value='bar', checked=True).render()
    u'<input type="hidden" name="foo" value="0"><input checked="checked" type="checkbox" id="field-foo_bar" value="bar" name="foo"/>'
    >>> Checkbox('foo', value='bar').render()
    u'<input type="hidden" name="foo" value="0"><input type="checkbox" id="field-foo_bar" value="bar" name="foo"/>'
    >>> c = Checkbox('foo', value='bar')
    >>> c.validate('on')
    True
    >>> c.render()
    u'<input type="hidden" name="foo" value="0"><input type="checkbox" id="field-foo_bar" value="bar" name="foo"/>'
    """

    def __init__(self, name, *validators, **attrs):
        self.checked = attrs.pop("checked", False)
        Input.__init__(self, name, *validators, **attrs)

    def get_default_id(self):
        value = safestr(self.value or "")
        return ID_TEMPLATE % self.name + "_" + value.replace(" ", "_")

    def render(self):
        attrs = self.attrs.copy()
        attrs["type"] = "checkbox"
        attrs["name"] = self.name
        attrs["value"] = self.value

        if self.checked:
            attrs["checked"] = "checked"
        return '<input type="hidden" name="%s" value="0">' "<input %s/>" % (attrs["name"], attrs)

    def set_value(self, value):
        self.checked = bool(value)

    def get_value(self):
        return self.checked class Dropdown(InputWithArgs):
    r"""Dropdown/select input.

        >>> Dropdown(name='foo', args=['a', 'b', 'c'], value='b').render()
        u'<input type="hidden" name="foo" value=""><select id="field-foo" name="foo">\n  <option value="a">a</option>\n  <option selected="selected" value="b">b</option>\n  <option value="c">c</option>\n</select>\n'
        >>> Dropdown(name='foo', args=[('a', 'aa'), ('b', 'bb'), ('c', 'cc')], value='b').render()
        u'<input type="hidden" name="foo" value=""><select id="field-foo" name="foo">\n  <option value="a">aa</option>\n  <option selected="selected" value="b">bb</option>\n  <option value="c">cc</option>\n</select>\n'
    """

    def render(self):
        attrs = self.attrs.copy()
        attrs["name"] = self.name
        # dummy value to post when no item is selected
        x = '<input type="hidden" name="%s" value="">' % self.name
        x += "<select %s>\n" % attrs

        for arg in self.args:
            x += self._render_option(arg)

        x += "</select>\n"
        return x

    def _render_option(self, arg, indent="  "):
        if isinstance(arg, (tuple, list)):
            value, desc = arg
        else:
            value, desc = arg, arg

        if self.value == value or (isinstance(self.value, list) and value in self.value):
            select_p = ' selected="selected"'
        else:
            select_p = ""
        return indent + '<option%s value="%s">%s</option>\n' % (
            select_p,
            websafe(value),
            websafe(desc),
        ) class PasswordWithHide(Password):
    @property
    def render_extra_after(self):
        return "<span class='password-toggle'><i class='fas fa-eye'></i></span>" class Radio(Input):
    def __init__(self, name, args, *validators, **attrs):
        self.args = args
        super(Radio, self).__init__(name, *validators, **attrs)

    def get_default_id(self):
        return ID_TEMPLATE % self.name + "_%s"

    def render(self):
        rendered = []
        for arg in self.args:
            if isinstance(arg, (tuple, list)):
                value, desc = arg
            else:
                value, desc = arg, arg
            attrs = self.attrs.copy()
            rendered_input = RadioSingle.render_single(value, self.name, self.value, attrs)
            rendered.append(
                '<label for="%s">%s %s</label>' % (attrs["id"], rendered_input, websafe(desc))
            )
        return '<div class="radio-inputs">%s</div>' % "\n".join(rendered) class Textbox(Input):
    """Textbox input.

        >>> Textbox(name='foo', value='bar').render()
        u'<input type="text" id="field-foo" value="bar" name="foo"/>'
        >>> Textbox(name='foo', value=0).render()
        u'<input type="text" id="field-foo" value="0" name="foo"/>'
    """

    def get_type(self):
        return "text" class HorizontalLine(object):
    def __init__(self, name, *validators, **attrs):
        self.name = name

        self.description = attrs.pop("description", name)
        self.value = attrs.pop("value", None)
        self.pre = attrs.pop("pre", "")
        self.post = attrs.pop("post", "")
        self.note = None
        self.required = attrs.pop("required", False)
        self.attrs = attrs = AttributeList(attrs)

        self.id = attrs.setdefault("id", self.get_default_id())

        if "class_" in attrs:
            attrs["class"] = attrs["class_"]
            del attrs["class_"]

    def is_hidden(self):
        return False

    def get_type(self):
        raise NotImplementedError

    def get_default_id(self):
        return "hr-%s" % self.name

    def validate(self, source, field_name=None):
        # Don't need to validate HorizontalLine
        return True

    def set_value(self, value):
        pass

    def get_value(self):
        pass

    def render(self):
        attrs = self.attrs.copy()
        return "<div %s><hr /></div>" % attrs

    def rendernote(self, note):
        return ""

    def addatts(self):
        # add leading space for backward-compatibility
        return " " + str(self.attrs)

    def __str__(self):
        self.render() class ForisState(object):
    def __init__(self):
    def update_lang(self, lang):
    def set_app(self, app):
    def set_backend(self, backend):
    def set_websocket(self, ws_port, ws_path, wss_port, wss_path):
    def update_reboot_required(self, required):
    def update_notification_count(self, new_count):
    def repr(self):
    def set_updater_is_running(self, running):
    def set_turris_os_version(self, version):
    def set_device(self, device):
    def update_password_set(self, password_set):
    def update_guide(self, guide_data):
    def set_assets_path(self, assets_path):
    def set_sentry(self, running): def reverse(name, **kargs):
    script_name, prefix = _get_prefix_and_script_name()
    try:
        path = bottle.app().router.build(name, **kargs)
        return _normalize_path_end("".join([script_name.rstrip("/"), path]))
    except bottle.RouteBuildError:
        for route in bottle.app().routes:
            if route.config.get("mountpoint"):
                config = route.config
                try:
                    prefix = config["mountpoint.prefix"].rstrip("/")
                    path = config["mountpoint.target"].router.build(name, **kargs)
                    return _normalize_path_end("".join([script_name.rstrip("/"), prefix, path]))
                except bottle.RouteBuildError as e:
                    if str(e).startswith("Missing URL"):
                        raise e
    raise bottle.RouteBuildError("No route with name '%s' in main app or mounted apps." % name) class _LangDict(collections.OrderedDict):
class SimpleDelayedTranslator(object):
class ForisInternationalizationExtension(InternationalizationExtension):
    def __missing__(self, key):
    def __init__(self, text):
    def __str__(self):
    def __add__(self, other):
def set_current_language(language):
    def __init__(self, environment): class BaseConfigHandler(object):
    def __init__(self, data=None):
        self.data = data
        self.__form_cache = None

    @property
    def form(self):
        if self.__form_cache is None:
            self.__form_cache = self.get_form()
        return self.__form_cache

    def get_form(self):
        """Get form. MUST be a single-section form.

        :return:
        :rtype: fapi.ForisForm
        """
        raise NotImplementedError()

    def save(self, extra_callbacks=None):
        """

        :param extra_callbacks: list of extra callbacks to call when saved
        :return:
        """
        form = self.form
        form.validate()
        if extra_callbacks:
            for cb in extra_callbacks:
                form.add_callback(cb)
        if form.valid:
            form.save()
            return True
        else:
            return False
import typing
from foris import fapi
from foris import validators
from foris.form import Checkbox, Dropdown, PasswordWithHide, Radio, Textbox, HorizontalLine
from foris.state import current_state
from foris.utils.routing import reverse
from foris.utils.translators import gettext_dummy as gettext, _
from .base import BaseConfigHandler
            return WifiEditForm.prefixed(device["id"], name)

        # get corresponding band
        bands = [e for e in device["available_bands"] if e["hwmode"] == form_data["hwmode"]]
        if not bands:
            # wrong hwmode selected pick the first one from available
            band = device["available_bands"][0]
            form_data["hwmode"] = device["available_bands"][0]["hwmode"]
        else:
            band = bands[0]

        wifi_main = section.add_section(name=prefixed("set_wifi"), title=None)
        wifi_main.add_field(
            Checkbox,
            name=prefixed("device_enabled"),
            label=_("Enable Wi-Fi %s") % (device["id"] + 1),
            default=True,
        )
        wifi_main.add_field(
            Textbox,
            name=prefixed("ssid"),
            label=_("SSID"),
            required=True,
            validators=validators.ByteLenRange(1, 32),
        ).requires(prefixed("device_enabled"), True)
        wifi_main.add_field(
            Checkbox,
            name=prefixed("ssid_hidden"),
            label=_("Hide SSID"),
            default=False,
            hint=_("If set, network is not visible when scanning for available networks."),
        ).requires(prefixed("device_enabled"), True)

        wifi_main.add_field(
            Radio,
            name=prefixed("hwmode"),
            label=_("Wi-Fi mode"),
            args=[
                e
                for e in (("11g", "2.4 GHz (g)"), ("11a", "5 GHz (a)"))
                if e[0] in [b["hwmode"] for b in device["available_bands"]]
            ],
            hint=_(
                "The 2.4 GHz band is more widely supported by clients, but "
                "tends to have more interference. The 5 GHz band is a newer"
                " standard and may not be supported by all your devices. It "
                "usually has less interference, but the signal does not "
                "carry so well indoors."
            ),
        ).requires(prefixed("device_enabled"), True)

        htmodes = (
            ("NOHT", _("Disabled")),
            ("HT20", _("802.11n - 20 MHz wide channel")),
            ("HT40", _("802.11n - 40 MHz wide channel")),
            ("VHT20", _("802.11ac - 20 MHz wide channel")),
            ("VHT40", _("802.11ac - 40 MHz wide channel")),
            ("VHT80", _("802.11ac - 80 MHz wide channel")),
            ("VHT160", _("802.11ac - 160 MHz wide channel")),
        )
        wifi_main.add_field(
            Dropdown,
            name=prefixed("htmode"),
            label=_("802.11n/ac mode"),
            args=[e for e in htmodes if e[0] in band["available_htmodes"]],
            hint=_(
                "Change this to adjust 802.11n/ac mode of operation. 802.11n with 40 MHz wide "
                "channels can yield higher throughput but can cause more interference in the "
                "network. If you don't know what to choose, use the default option with 20 MHz "
                "wide channel."
            ),
        ).requires(prefixed("device_enabled"), True).requires(
            prefixed("hwmode"), lambda val: val in ("11g", "11a")
        )  # this req is added to rerender htmodes when hwmode changes

        channels = [("0", _("auto"))] + [
            (
                str(e["number"]),
                ("%d (%d MHz%s)" % (e["number"], e["frequency"], ", DFS" if e["radar"] else "")),
            )
            for e in band["available_channels"]
        ]
        wifi_main.add_field(
            Dropdown,
            name=prefixed("channel"),
            label=_("Network channel"),
            default="0",
            args=channels,
        ).requires(prefixed("device_enabled"), True).requires(
            prefixed("hwmode"), lambda val: val in ("11g", "11a")
        )  # this req is added to rerender channel list when hwmode changes

        wifi_main.add_field(
            PasswordWithHide,
            name=prefixed("password"),
            label=_("Network password"),
            required=True,
            validators=validators.ByteLenRange(8, 63),
            hint=HINTS["password"],
        ).requires(prefixed("device_enabled"), True)

        if current_state.app == "config" and self.enable_guest:
            # Guest wi-fi part
            guest_section = wifi_main.add_section(
                name=prefixed("set_guest_wifi"),
                title=_("Guest Wi-Fi"),
                description=_("Set guest Wi-Fi here."),
            )
            guest_section.add_field(
                Checkbox,
                name=prefixed("guest_enabled"),
                label=_("Enable guest Wi-Fi"),
                default=False,
                hint=_(
                    "Enables Wi-Fi for guests, which is separated from LAN network. Devices "
                    "connected to this network are allowed to access the internet, but aren't "
                    "allowed to access other devices and the configuration interface of the "
                    "router. Parameters of the guest network can be set in <a href='%(url)s'>the "
                    "Guest network tab</a>."
                )

                % dict(url=reverse("config_page", page_name="guest")),