Merge pull request #2688 from lemoer/pr_config_mode_ui_for_interface_roles_v2
Config-Mode UI for Interface Role Assignment (v2)
This commit is contained in:
commit
10e52bec3a
@ -29,6 +29,7 @@ files["package/**/check_site.lua"] = {
|
||||
"need",
|
||||
"need_alphanumeric_key",
|
||||
"need_array",
|
||||
"need_array_elements_exclusive",
|
||||
"need_array_of",
|
||||
"need_boolean",
|
||||
"need_chanlist",
|
||||
@ -50,6 +51,7 @@ files["package/**/check_site.lua"] = {
|
||||
|
||||
files["package/**/luasrc/lib/gluon/config-mode/*"] = {
|
||||
globals = {
|
||||
"MultiListValue",
|
||||
"DynamicList",
|
||||
"Flag",
|
||||
"Form",
|
||||
|
@ -448,13 +448,8 @@ interfaces \: optional
|
||||
The ``client`` role requires exclusive control over an interface. When
|
||||
the ``client`` role is assigned to an interface at the same time as other
|
||||
roles (like ``'client', 'mesh'`` in the above example), the other roles take
|
||||
precedence (enabling ``mesh``, but not ``client`` in the example).
|
||||
|
||||
Such a default configuration still fulfills a purpose (and is in fact the
|
||||
recommended way to enable "Mesh-on-LAN" by default): The "LAN interface
|
||||
meshing" checkbox in the advanced network settings will only add or remove
|
||||
the ``mesh`` role, so the ``client`` role must already be in the configuration
|
||||
to make the LAN port a regular client interface when the checkbox is disabled.
|
||||
precedence (enabling ``mesh``, but not ``client`` in the example). In that
|
||||
case, the ``client`` role is removed from the config of the interface.
|
||||
|
||||
All interface settings are optional. If unset, the following defaults are
|
||||
used:
|
||||
|
File diff suppressed because one or more lines are too long
@ -273,6 +273,10 @@ input[type=checkbox] {
|
||||
text-align: center;
|
||||
font-size: 1.7em;
|
||||
}
|
||||
|
||||
&[disabled] + label {
|
||||
background-color: #dcdcdc !important;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=radio] {
|
||||
@ -366,6 +370,13 @@ input[type=password] {
|
||||
min-width: 20em;
|
||||
}
|
||||
|
||||
.gluon-multi-list-option-descr {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-top: 0.35em;
|
||||
margin-left: 0.4em;
|
||||
}
|
||||
|
||||
.gluon-button {
|
||||
@include button;
|
||||
|
||||
|
@ -77,7 +77,11 @@ need_boolean(in_domain({'mesh', 'vxlan'}), false)
|
||||
|
||||
local interfaces_roles = {'client', 'uplink', 'mesh'}
|
||||
for _, config in ipairs({'wan', 'lan', 'single'}) do
|
||||
need_array_of(in_site({'interfaces', config, 'default_roles'}), interfaces_roles, false)
|
||||
local default_roles = in_site({'interfaces', config, 'default_roles'})
|
||||
|
||||
need_array_of(default_roles, interfaces_roles, false)
|
||||
need_array_elements_exclusive(default_roles, 'client', 'mesh', false)
|
||||
need_array_elements_exclusive(default_roles, 'client', 'uplink', false)
|
||||
end
|
||||
|
||||
obsolete({'mesh_on_wan'}, 'Use interfaces.wan.default_roles.')
|
||||
|
@ -55,6 +55,14 @@ local function merge(a, b)
|
||||
return m
|
||||
end
|
||||
|
||||
local function contains(table, val)
|
||||
for i=1,#table do
|
||||
if table[i] == val then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function path_to_string(path)
|
||||
if path.is_value then
|
||||
@ -370,6 +378,21 @@ function M.need_array_of(path, array, required)
|
||||
return M.need_array(path, function(e) M.need_one_of(e, array) end, required)
|
||||
end
|
||||
|
||||
function M.need_array_elements_exclusive(path, a, b, required)
|
||||
local val = need_type(path, 'table', required, 'be an array')
|
||||
if not val then
|
||||
return nil
|
||||
end
|
||||
|
||||
if contains(val, a) and contains(val, b) then
|
||||
config_error(conf_src(path),
|
||||
'expected %s to contain only one of the elements %s and %s, but not both.',
|
||||
path_to_string(path), format(a), format(b))
|
||||
end
|
||||
|
||||
return val
|
||||
end
|
||||
|
||||
function M.need_chanlist(path, channels, required)
|
||||
local valid_chanlist = check_chanlist(channels)
|
||||
return M.need(path, valid_chanlist, required,
|
||||
|
@ -63,4 +63,19 @@ for iface in pairs(interfaces) do
|
||||
end
|
||||
end
|
||||
|
||||
-- Fix invalid role configurations
|
||||
|
||||
uci:foreach('gluon', 'interface', function(interface)
|
||||
|
||||
local function has_role(role)
|
||||
return util.contains(interface.role, role)
|
||||
end
|
||||
|
||||
if has_role('client') and (has_role('mesh') or has_role('uplink')) then
|
||||
-- remove 'client' role
|
||||
util.remove_from_set(interface.role, 'client')
|
||||
uci:set('gluon', interface['.name'], 'role', interface.role)
|
||||
end
|
||||
end)
|
||||
|
||||
uci:save('gluon')
|
||||
|
@ -0,0 +1,25 @@
|
||||
<%
|
||||
local br = self.orientation == "horizontal" and '   ' or '<br>'
|
||||
local entries = self:entries()
|
||||
local util = require 'gluon.util'
|
||||
%>
|
||||
<div>
|
||||
<% for i, entry in pairs(entries) do %>
|
||||
<label<%=
|
||||
attr("data-index", i) ..
|
||||
attr("data-depends", self:deplist(entry.deps))
|
||||
%>>
|
||||
<input data-update="click change" type="checkbox"<%=
|
||||
attr("id", id.."."..entry.key) ..
|
||||
attr("name", id) ..
|
||||
attr("value", entry.key) ..
|
||||
attr("checked", (util.contains(self:cfgvalue(), entry.key)) and "checked") ..
|
||||
attr("data-exclusive-with", self.exclusions[entry.key]) ..
|
||||
attr("data-update", "change")
|
||||
%>>
|
||||
<label<%= attr("for", id.."."..entry.key)%>></label>
|
||||
<span class="gluon-multi-list-option-descr"><%|entry.value%></span>
|
||||
</label>
|
||||
<% if i ~= #entries then write(br) end %>
|
||||
<% end %>
|
||||
</div>
|
@ -1,19 +1,15 @@
|
||||
/*
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008-2012 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
SPDX-FileCopyrightText: 2008, Steven Barth <steven@midlink.org>
|
||||
SPDX-FileCopyrightText: 2008-2012, Jo-Philipp Wich <jow@openwrt.org>
|
||||
SPDX-FileCopyrightText: 2017, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
SPDX-FileCopyrightText: 2023, Leonardo Mörlein <me@irrelefant.net>
|
||||
*/
|
||||
|
||||
/*
|
||||
Build using:
|
||||
|
||||
uglifyjs javascript/gluon-web-model.js -o javascript/gluon-web-model.min.js -c -m --support-ie8
|
||||
uglifyjs javascript/gluon-web-model.js -o javascript/gluon-web-model.min.js -c -m --ie
|
||||
*/
|
||||
|
||||
|
||||
@ -219,6 +215,20 @@
|
||||
parent.parentNode.style.display = (parent.options.length <= 1) ? 'none' : '';
|
||||
}
|
||||
|
||||
var nodes = document.querySelectorAll('[data-exclusive-with]');
|
||||
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
|
||||
var exclusive_with = JSON.parse(node.getAttribute('data-exclusive-with'));
|
||||
|
||||
node.disabled = false;
|
||||
for (var list_item of exclusive_with) {
|
||||
var el = document.getElementById(node.name + '.' + list_item);
|
||||
node.disabled ||= el.checked;
|
||||
}
|
||||
|
||||
if (node.disabled)
|
||||
node.checked = false;
|
||||
}
|
||||
|
||||
if (state) {
|
||||
update();
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,8 +1,10 @@
|
||||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
-- SPDX-FileCopyrightText: 2008, Steven Barth <steven@midlink.org>
|
||||
-- SPDX-FileCopyrightText: 2017-2018, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
-- SPDX-FileCopyrightText: 2023, Leonardo Mörlein <me@irrelefant.net>
|
||||
|
||||
local util = require "gluon.web.util"
|
||||
local gluon_util = require "gluon.util"
|
||||
|
||||
local datatypes = require "gluon.web.model.datatypes"
|
||||
local class = util.class
|
||||
@ -361,6 +363,83 @@ function ListValue:validate()
|
||||
end
|
||||
|
||||
|
||||
local MultiListValue = class(AbstractValue)
|
||||
M.MultiListValue = MultiListValue
|
||||
|
||||
function MultiListValue:__init__(...)
|
||||
AbstractValue.__init__(self, ...)
|
||||
self.subtemplate = "model/mlvalue"
|
||||
|
||||
self.size = 1
|
||||
|
||||
self.keys = {}
|
||||
self.entry_list = {}
|
||||
end
|
||||
|
||||
function MultiListValue:value(key, val, ...)
|
||||
key = tostring(key)
|
||||
|
||||
if self.keys[key] then
|
||||
return
|
||||
end
|
||||
self.keys[key] = true
|
||||
self.exclusions = {}
|
||||
|
||||
val = val or key
|
||||
table.insert(self.entry_list, {
|
||||
key = key,
|
||||
value = tostring(val),
|
||||
deps = {...},
|
||||
})
|
||||
end
|
||||
|
||||
function MultiListValue:entries()
|
||||
local ret = {unpack(self.entry_list)}
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
function MultiListValue:validate()
|
||||
for _, val in ipairs(self.data) do
|
||||
if not self.keys[val] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
for key, exclusive_with in pairs(self.exclusions) do
|
||||
if gluon_util.contains(self.data, key) then
|
||||
for _, exclusion in ipairs(exclusive_with) do
|
||||
if gluon_util.contains(self.data, exclusion) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function MultiListValue:exclusive(a, b)
|
||||
if not self.exclusions[a] then
|
||||
self.exclusions[a] = {}
|
||||
end
|
||||
if not self.exclusions[b] then
|
||||
self.exclusions[b] = {}
|
||||
end
|
||||
|
||||
gluon_util.add_to_set(self.exclusions[a], b)
|
||||
gluon_util.add_to_set(self.exclusions[b], a)
|
||||
end
|
||||
|
||||
function MultiListValue:defaultvalue()
|
||||
return self.default or {}
|
||||
end
|
||||
|
||||
function MultiListValue:formvalue(http)
|
||||
return http:formvaluetable(self:id())
|
||||
end
|
||||
|
||||
|
||||
local DynamicList = class(AbstractValue)
|
||||
M.DynamicList = DynamicList
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
-- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
-- SPDX-FileCopyrightText: 2010, Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- SPDX-FileCopyrightText: 2017, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
|
||||
local M = {}
|
||||
|
||||
|
@ -28,15 +28,6 @@ msgstr "PoE-Passthrough aktivieren"
|
||||
msgid "Enable PoE Power Port %s"
|
||||
msgstr "PoE-Ausgabe auf Port %s aktivieren"
|
||||
|
||||
msgid "Enable meshing on the Ethernet interface"
|
||||
msgstr "Mesh auf dem Ethernet-Port aktivieren"
|
||||
|
||||
msgid "Enable meshing on the LAN interface"
|
||||
msgstr "Mesh auf dem LAN-Port aktivieren"
|
||||
|
||||
msgid "Enable meshing on the WAN interface"
|
||||
msgstr "Mesh auf dem WAN-Port aktivieren"
|
||||
|
||||
msgid "Gateway"
|
||||
msgstr "Gateway"
|
||||
|
||||
@ -49,6 +40,12 @@ msgstr "IPv4"
|
||||
msgid "IPv6"
|
||||
msgstr "IPv6"
|
||||
|
||||
msgid "Interface"
|
||||
msgstr "Interface"
|
||||
|
||||
msgid "LAN Interfaces"
|
||||
msgstr "LAN-Interfaces"
|
||||
|
||||
msgid "Netmask"
|
||||
msgstr "Netzmaske"
|
||||
|
||||
@ -61,5 +58,8 @@ msgstr "Statisch"
|
||||
msgid "Static DNS servers"
|
||||
msgstr "Statische DNS-Server"
|
||||
|
||||
msgid "WAN Interfaces"
|
||||
msgstr "WAN-Interfaces"
|
||||
|
||||
msgid "WAN connection"
|
||||
msgstr "WAN-Verbindung"
|
||||
|
@ -28,15 +28,6 @@ msgstr ""
|
||||
msgid "Enable PoE Power Port %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable meshing on the Ethernet interface"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable meshing on the LAN interface"
|
||||
msgstr "Activer le réseau MESH sur le port LAN"
|
||||
|
||||
msgid "Enable meshing on the WAN interface"
|
||||
msgstr "Activer le réseau MESH sur les ports WAN"
|
||||
|
||||
msgid "Gateway"
|
||||
msgstr "Passerelle"
|
||||
|
||||
|
@ -19,15 +19,6 @@ msgstr ""
|
||||
msgid "Enable PoE Power Port %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable meshing on the Ethernet interface"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable meshing on the LAN interface"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable meshing on the WAN interface"
|
||||
msgstr ""
|
||||
|
||||
msgid "Gateway"
|
||||
msgstr ""
|
||||
|
||||
@ -40,6 +31,12 @@ msgstr ""
|
||||
msgid "IPv6"
|
||||
msgstr ""
|
||||
|
||||
msgid "Interface"
|
||||
msgstr ""
|
||||
|
||||
msgid "LAN Interfaces"
|
||||
msgstr ""
|
||||
|
||||
msgid "Netmask"
|
||||
msgstr ""
|
||||
|
||||
@ -52,5 +49,8 @@ msgstr ""
|
||||
msgid "Static DNS servers"
|
||||
msgstr ""
|
||||
|
||||
msgid "WAN Interfaces"
|
||||
msgstr ""
|
||||
|
||||
msgid "WAN connection"
|
||||
msgstr ""
|
||||
|
@ -1,21 +1,14 @@
|
||||
--[[
|
||||
Copyright 2014 Nils Schneider <nils@nilsschneider.net>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
]]--
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
-- SPDX-FileCopyrightText: 2014, Nils Schneider <nils@nilsschneider.net>
|
||||
-- SPDX-FileCopyrightText: 2023, Leonardo Mörlein <me@irrelefant.net>
|
||||
|
||||
local uci = require("simple-uci").cursor()
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
local util = require 'gluon.util'
|
||||
|
||||
local wan = uci:get_all("network", "wan")
|
||||
local wan6 = uci:get_all("network", "wan6")
|
||||
local dns_static = uci:get_first("gluon-wan-dnsmasq", "static")
|
||||
|
||||
|
||||
local f = Form(translate("WAN connection"))
|
||||
|
||||
local s = f:section(Section)
|
||||
@ -76,36 +69,30 @@ end
|
||||
|
||||
s = f:section(Section)
|
||||
|
||||
local wired_mesh_help = {
|
||||
single = translate('Enable meshing on the Ethernet interface'),
|
||||
wan = translate('Enable meshing on the WAN interface'),
|
||||
lan = translate('Enable meshing on the LAN interface'),
|
||||
local pretty_ifnames = {
|
||||
["/wan"] = translate("WAN Interfaces"),
|
||||
["/single"] = translate("Interface"),
|
||||
["/lan"] = translate("LAN Interfaces")
|
||||
}
|
||||
|
||||
local function wired_mesh(iface)
|
||||
if not sysconfig[iface .. '_ifname'] then return end
|
||||
local iface_roles = uci:get_list('gluon', 'iface_' .. iface, 'role')
|
||||
uci:foreach('gluon', 'interface', function(config)
|
||||
local section_name = config['.name']
|
||||
local ifaces = s:option(MultiListValue, section_name, pretty_ifnames[config.name] or config.name)
|
||||
|
||||
local option = s:option(Flag, 'mesh_' .. iface, wired_mesh_help[iface])
|
||||
option.default = util.contains(iface_roles, 'mesh') ~= false
|
||||
ifaces.orientation = 'horizontal'
|
||||
ifaces:value('uplink', 'Uplink')
|
||||
ifaces:value('mesh', 'Mesh')
|
||||
ifaces:value('client', 'Client')
|
||||
ifaces:exclusive('uplink', 'client')
|
||||
ifaces:exclusive('mesh', 'client')
|
||||
|
||||
function option:write(data)
|
||||
local roles = uci:get_list('gluon', 'iface_' .. iface, 'role')
|
||||
if data then
|
||||
util.add_to_set(roles, 'mesh')
|
||||
else
|
||||
util.remove_from_set(roles, 'mesh')
|
||||
end
|
||||
uci:set_list('gluon', 'iface_' .. iface, 'role', roles)
|
||||
ifaces.default = config.role
|
||||
|
||||
-- Reconfigure on next reboot
|
||||
uci:set('gluon', 'core', 'reconfigure', true)
|
||||
function ifaces:write(data)
|
||||
uci:set_list("gluon", section_name, "role", data)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
wired_mesh('single')
|
||||
wired_mesh('wan')
|
||||
wired_mesh('lan')
|
||||
|
||||
local section
|
||||
uci:foreach("system", "gpio_switch", function(si)
|
||||
@ -166,4 +153,5 @@ function f:write()
|
||||
uci:commit('system')
|
||||
end
|
||||
|
||||
|
||||
return f
|
||||
|
Loading…
Reference in New Issue
Block a user