Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 0x4A6F-master
  • 0x4A6F-rpi4
  • autinerd/experimental-openwrt-24.10
  • experimental
  • feature/addMikrotikwAP
  • master
  • nrb/airmax-test
  • nrb/ar9344-reset-sequence
  • nrb/ex400-remove-wps
  • nrb/gluon-master-cpe510
  • nrb/test-radv-filter
  • nrbffs/fastd-remove-delay
  • nrbffs/netgear-ex6120
  • v2018.2.2-ffs
  • v2018.2.3-ffs
  • v2019.1-ffs
  • v2019.1.1-ffs
  • v2019.1.2-ffs
  • v2020.1-ffs
  • v2020.1.1-ffs
  • v2020.1.3-ffs
  • v2020.2-ffs
  • v2020.2.1-ffs
  • v2020.2.2-ffs
  • v2020.2.3-ffs
  • v2021.1-ffs
  • v2021.1.1-ffs
  • v2021.1.2-ffs
  • v2022.1.1-ffs
  • v2022.1.3-ffs
  • v2022.1.4-ffs
  • v2023.1-ffs
  • v2023.2-ffs
  • v2023.2.2-ffs
  • v2023.2.3-ffs
  • v2023.2.4-ffs
  • v2023.2.5-ffs
  • experimental-2022-09-24
  • experimental-2022-09-24-base
  • experimental-2023-03-11
  • experimental-2023-03-11-base
  • experimental-2023-03-12
  • experimental-2023-03-12-base
  • experimental-2023-03-16
  • experimental-2023-03-16-base
  • experimental-2023-03-20
  • experimental-2023-03-20-base
  • experimental-2023-03-23
  • experimental-2023-03-23-base
  • experimental-2023-03-25
  • experimental-2023-03-25-base
  • experimental-2023-03-26
  • experimental-2023-03-26-base
  • experimental-2023-03-30
  • experimental-2023-03-30-base
  • experimental-2023-03-31
  • experimental-2023-03-31-base
  • experimental-2023-04-01
  • experimental-2023-04-01-base
  • experimental-2023-04-08
  • experimental-2023-04-08-base
  • experimental-2023-04-10
  • experimental-2023-04-10-base
  • experimental-2023-04-13
  • experimental-2023-04-13-base
  • experimental-2023-04-15
  • experimental-2023-04-15-base
  • experimental-2023-04-16
  • experimental-2023-04-16-base
  • experimental-2023-04-18
  • experimental-2023-04-18-base
  • experimental-2023-04-20
  • experimental-2023-04-20-base
  • experimental-2023-04-26
  • experimental-2023-04-26-base
  • experimental-2023-04-28
  • experimental-2023-04-28-base
  • experimental-2023-04-30
  • experimental-2023-04-30-base
  • experimental-2023-05-02
  • experimental-2023-05-02-base
  • experimental-2023-05-03
  • experimental-2023-05-03-base
  • experimental-2023-05-12
  • experimental-2023-05-12-base
  • experimental-2023-05-21
  • experimental-2023-05-21-base
  • experimental-2023-05-25
  • experimental-2023-05-25-base
  • experimental-2023-07-02
  • experimental-2023-07-02-base
  • experimental-2023-07-04
  • experimental-2023-07-04-base
  • experimental-2023-07-12
  • experimental-2023-07-12-base
  • experimental-2023-07-16
  • experimental-2023-07-16-base
  • experimental-2023-08-04
  • experimental-2023-08-04-base
  • experimental-2023-08-10
  • experimental-2023-08-10-base
  • experimental-2023-09-08
  • experimental-2023-09-08-base
  • experimental-2023-09-09
  • experimental-2023-09-09-base
  • experimental-2023-09-10
  • experimental-2023-09-10-base
  • experimental-2023-09-11
  • experimental-2023-09-11-base
  • experimental-2023-09-12
  • experimental-2023-09-12-base
  • experimental-2023-09-13
  • experimental-2023-09-13-base
  • experimental-2023-09-15
  • experimental-2023-09-15-base
  • experimental-2023-09-16
  • experimental-2023-09-16-base
  • experimental-2023-09-18
  • experimental-2023-09-18-base
  • experimental-2023-09-20
  • experimental-2023-09-20-base
  • experimental-2023-09-27
  • experimental-2023-09-27-base
  • experimental-2023-09-28
  • experimental-2023-09-28-base
  • experimental-2023-09-29
  • experimental-2023-09-29-base
  • experimental-2023-10-02
  • experimental-2023-10-02-base
  • experimental-2023-10-13
  • experimental-2023-10-13-base
  • experimental-2023-10-14
  • experimental-2023-10-14-base
  • experimental-2023-10-16
  • experimental-2023-10-16-base
  • experimental-2023-10-23
  • experimental-2023-10-23-base
137 results

Target

Select target project
  • firmware/gluon
  • 0x4A6F/gluon
  • patrick/gluon
3 results
Select Git revision
  • 0x4A6F-master
  • 0x4A6F-rpi4
  • 2014.3.x
  • 2014.4.x
  • babel
  • experimental
  • hoodselector
  • master
  • nrb/gluon-master-cpe510
  • nrb/test-radv-filter
  • nrbffs/fastd-remove-delay
  • nrbffs/netgear-ex6120
  • radv-filterd
  • v2015.1.x
  • v2016.1.x
  • v2016.2.4-batmanbug
  • v2016.2.x
  • v2018.2.2-ffs
  • v2018.2.3-ffs
  • v2018.2.x
  • v2019.1-ffs
  • v2019.1.1-ffs
  • v2019.1.2-ffs
  • v2020.1-ffs
  • v2020.1.1-ffs
  • v2020.1.3-ffs
  • v2020.2-ffs
  • v2020.2.1-ffs
  • v2020.2.2-ffs
  • v2020.2.3-ffs
  • v2020.2.x
  • v2021.1-ffs
  • v2021.1.1-ffs
  • v2021.1.2-ffs
  • v2014.1
  • v2014.2
  • v2014.3
  • v2014.3.1
  • v2014.4
  • v2015.1
  • v2015.1.1
  • v2015.1.2
  • v2016.1
  • v2016.1.1
  • v2016.1.2
  • v2016.1.3
  • v2016.1.4
  • v2016.1.5
  • v2016.1.6
  • v2016.2
  • v2016.2.1
  • v2016.2.2
  • v2016.2.3
  • v2016.2.4
  • v2016.2.5
  • v2016.2.6
  • v2016.2.7
  • v2017.1
  • v2017.1.1
  • v2017.1.2
  • v2017.1.3
  • v2017.1.4
  • v2017.1.5
  • v2017.1.6
  • v2017.1.7
  • v2017.1.8
  • v2018.1
  • v2018.1.1
  • v2018.1.2
  • v2018.1.3
  • v2018.1.4
  • v2018.2
  • v2018.2-ffs0.1
  • v2018.2.1
  • v2018.2.1-ffs0.1
  • v2018.2.2-ffs0.1
  • v2018.2.3-ffs0.1
  • v2019.1-ffs0.1
  • v2019.1.1-ffs0.1
  • v2019.1.2-ffs0.1
  • v2020.1-ffs0.1
  • v2020.1.1-ffs0.1
  • v2020.1.3-ffs0.1
  • v2020.2
  • v2020.2-ffs0.1
  • v2020.2.1-ffs0.1
  • v2020.2.2-ffs0.1
  • v2020.2.3-ffs0.1
  • v2020.2.3-ffs0.2
  • v2020.2.3-ffs0.3
  • v2020.2.x-ffs0.1
  • v2021.1-ffs0.1
  • v2021.1.1-ffs0.1
  • v2021.1.1-ffs0.2
  • v2021.1.1-ffs0.3
  • v2021.1.1-ffs0.4
  • v2021.1.2-ffs0.1
  • v2021.1.2-ffs0.2
98 results
Show changes
Showing
with 624 additions and 319 deletions
msgid "" msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8" msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "One or more fields contain invalid values!"
msgstr ""
msgid "One or more required fields have no value!"
msgstr ""
msgid "Reset" msgid "Reset"
msgstr "" msgstr ""
......
/* /*
Copyright 2008 Steven Barth <steven@midlink.org> SPDX-License-Identifier: Apache-2.0
Copyright 2008-2012 Jo-Philipp Wich <jow@openwrt.org> SPDX-FileCopyrightText: 2008, Steven Barth <steven@midlink.org>
Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net> SPDX-FileCopyrightText: 2008-2012, Jo-Philipp Wich <jow@openwrt.org>
SPDX-FileCopyrightText: 2017, Matthias Schiffer <mschiffer@universe-factory.net>
Licensed under the Apache License, Version 2.0 (the "License"); SPDX-FileCopyrightText: 2023, Leonardo Mörlein <me@irrelefant.net>
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
*/ */
/* /*
Build using: Build using:
uglifyjs javascript/gluon-web-model.js -o files/lib/gluon/web/www/static/gluon-web-model.js -c -m --support-ie8 uglifyjs javascript/gluon-web-model.js -o javascript/gluon-web-model.min.js -c -m --ie
*/ */
...@@ -183,6 +179,8 @@ ...@@ -183,6 +179,8 @@
} }
function update() { function update() {
window.dispatchEvent(new Event('gluon-update'));
var state = false; var state = false;
for (var id in dep_entries) { for (var id in dep_entries) {
var entry = dep_entries[id]; var entry = dep_entries[id];
...@@ -191,6 +189,7 @@ ...@@ -191,6 +189,7 @@
if (node && node.parentNode && !check(entry.deps)) { if (node && node.parentNode && !check(entry.deps)) {
node.parentNode.removeChild(node); node.parentNode.removeChild(node);
node.dispatchEvent(new Event('gluon-hide'));
state = true; state = true;
} else if (parent && (!node || !node.parentNode) && check(entry.deps)) { } else if (parent && (!node || !node.parentNode) && check(entry.deps)) {
var next = undefined; var next = undefined;
...@@ -207,6 +206,7 @@ ...@@ -207,6 +206,7 @@
parent.insertBefore(entry.node, next); parent.insertBefore(entry.node, next);
} }
entry.node.dispatchEvent(new Event('gluon-show'));
state = true; state = true;
} }
...@@ -215,6 +215,20 @@ ...@@ -215,6 +215,20 @@
parent.parentNode.style.display = (parent.options.length <= 1) ? 'none' : ''; 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) { if (state) {
update(); update();
} }
...@@ -272,7 +286,6 @@ ...@@ -272,7 +286,6 @@
t.value = values[i-1]; t.value = values[i-1];
t.type = 'text'; t.type = 'text';
t.index = i; t.index = i;
t.className = 'gluon-input-text';
if (attr.size) if (attr.size)
t.size = attr.size; t.size = attr.size;
...@@ -471,6 +484,7 @@ ...@@ -471,6 +484,7 @@
bind(field, "blur", validator); bind(field, "blur", validator);
bind(field, "keyup", validator); bind(field, "keyup", validator);
bind(field, "gluon-revalidate", validator);
if (field.nodeName.toLowerCase() == 'select') { if (field.nodeName.toLowerCase() == 'select') {
bind(field, "change", validator); bind(field, "change", validator);
...@@ -512,7 +526,7 @@ ...@@ -512,7 +526,7 @@
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) { for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
var events = node.getAttribute('data-update').split(' '); var events = node.getAttribute('data-update').split(' ');
for (var j = 0, event; (event = events[j]) !== undefined; j++) { for (var j = 0, event; (event = events[j]) !== undefined; j++) {
bind(node, event, update); bind(node, event, function () {setTimeout(update, 0);});
} }
} }
......
!function(){var s={};function a(e){return/^-?\d+$/.test(e)?+e:NaN}function i(e){return/^-?\d*\.?\d+?$/.test(e)?+e:NaN}var d={integer:function(){return!isNaN(a(this))},uinteger:function(){return 0<=a(this)},"float":function(){return!isNaN(i(this))},ufloat:function(){return 0<=i(this)},ipaddr:function(){return d.ip4addr.apply(this)||d.ip6addr.apply(this)},ip4addr:function(){var e;return!!(e=this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/))&&0<=e[1]&&e[1]<=255&&0<=e[2]&&e[2]<=255&&0<=e[3]&&e[3]<=255&&0<=e[4]&&e[4]<=255},ip6addr:function(){return this.indexOf("::")<0?null!=this.match(/^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$/i):!(0<=this.indexOf(":::")||this.match(/::.+::/)||this.match(/^:[^:]/)||this.match(/[^:]:$/)||!this.match(/^(?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}$/i)&&!this.match(/^(?:[a-f0-9]{1,4}:){7}:$/i)&&!this.match(/^:(?::[a-f0-9]{1,4}){7}$/i))},wpakey:function(){var e=this;return 64==e.length?null!=e.match(/^[a-f0-9]{64}$/i):8<=e.length&&e.length<=63},range:function(e,t){var n=i(this);return+e<=n&&n<=+t},min:function(e){return i(this)>=+e},max:function(e){return i(this)<=+e},irange:function(e,t){var n=a(this);return+e<=n&&n<=+t},imin:function(e){return a(this)>=+e},imax:function(e){return a(this)<=+e},minlength:function(e){return+e<=(""+this).length},maxlength:function(e){return(""+this).length<=+e}};function p(e){for(var t,n,a,i=0;i<e.length;i++){var r,d=!0;for(r in e[i])d=d&&(t=r,n=e[i][r],a=void 0,(a=document.getElementById(t))?("checkbox"==a.type?a.checked:a.value||"")==n:!!(a=document.getElementById(t+"."+n))&&"radio"==a.type&&a.checked);if(d)return 1}}function f(){window.dispatchEvent(new Event("gluon-update"));var e,t=!1;for(e in s){var n=s[e],a=document.getElementById(e),i=document.getElementById(n.parent);if(a&&a.parentNode&&!p(n.deps))a.parentNode.removeChild(a),a.dispatchEvent(new Event("gluon-hide")),t=!0;else if(i&&(!a||!a.parentNode)&&p(n.deps)){for(var r=undefined,r=i.firstChild;r&&!(r.getAttribute&&parseInt(r.getAttribute("data-index"),10)>n.index);r=r.nextSibling);r?i.insertBefore(n.node,r):i.appendChild(n.node),n.node.dispatchEvent(new Event("gluon-show")),t=!0}i&&i.parentNode&&i.getAttribute("data-optionals")&&(i.parentNode.style.display=i.options.length<=1?"none":"")}for(var d=document.querySelectorAll("[data-exclusive-with]"),o=0;(a=d[o])!==undefined;o++){var u,l=JSON.parse(a.getAttribute("data-exclusive-with"));a.disabled=!1;for(u of l){var c=document.getElementById(a.name+"."+u);a.disabled||=c.checked}a.disabled&&(a.checked=!1)}t&&f()}function v(e,t,n,a){e.addEventListener?e.addEventListener(t,n,!!a):e.attachEvent("on"+t,function(){var e=window.event;return!e.target&&e.srcElement&&(e.target=e.srcElement),!!n(e)})}function m(t,n,e){var a,i,r=(i=(e=e).match(/^([^\(]+)\(([^,]+),([^\)]+)\)$/))&&(a=d[i[1]])!==undefined?function(){return a.apply(this,[i[2],i[3]])}:(i=e.match(/^([^\(]+)\(([^,\)]+)\)$/))&&(a=d[i[1]])!==undefined?function(){return a.apply(this,[i[2]])}:d[e];r&&(v(t,"blur",e=function(){var e;t.form&&(t.className=t.className.replace(/ gluon-input-invalid/g,""),0==(e=(t.options&&-1<t.options.selectedIndex?t.options[t.options.selectedIndex]:t).value).length&&n||r.apply(e)||(t.className+=" gluon-input-invalid"))}),v(t,"keyup",e),v(t,"gluon-revalidate",e),"select"==t.nodeName.toLowerCase()&&(v(t,"change",e),v(t,"click",e)),e())}for(var e,t,n,r,o=document.querySelectorAll("[data-depends]"),u=0;(b=o[u])!==undefined;u++){var l=parseInt(b.getAttribute("data-index"),10),c=JSON.parse(b.getAttribute("data-depends"));if(!isNaN(l)&&0<c.length)for(var h=0;h<c.length;h++)e=b,t=c[h],n=l,r=void 0,(r=s[e.id])||(r={node:e,parent:e.parentNode.id,deps:[],index:n},s[e.id]=r),r.deps.push(t)}for(o=document.querySelectorAll("[data-update]"),u=0;(b=o[u])!==undefined;u++)for(var g,y=b.getAttribute("data-update").split(" "),N=0;(g=y[N])!==undefined;N++)v(b,g,function(){setTimeout(f,0)});for(o=document.querySelectorAll("[data-type]"),u=0;(b=o[u])!==undefined;u++)m(b,"true"===b.getAttribute("data-optional"),b.getAttribute("data-type"));o=document.querySelectorAll("[data-dynlist]");for(var b,u=0;(b=o[u])!==undefined;u++){var w=JSON.parse(b.getAttribute("data-dynlist"));!function(l,c){var s=c.prefix;function d(e,t,n){for(var a=[];l.firstChild;){var i=l.firstChild;(r=+i.index)!=n&&("input"==i.nodeName.toLowerCase()?a.push(i.value||""):"select"==i.nodeName.toLowerCase()&&(a[a.length-1]=i.options[i.selectedIndex].value)),l.removeChild(i)}0<=t?(e=t+1,a.splice(t,0,"")):c.optional||0!=a.length||a.push("");for(var r=1;r<=a.length;r++){var d,o,u=document.createElement("input");u.id=s+"."+r,u.name=s,u.value=a[r-1],u.type="text",u.index=r,c.size&&(u.size=c.size),c.placeholder&&(u.placeholder=c.placeholder),l.appendChild(u),c.type&&m(u,!1,c.type),v(u,"keydown",f),v(u,"keypress",p),r==e?u.focus():-r==e&&(u.focus(),d=u.value,u.value=" ",u.value=d),(c.optional||1<a.length)&&((o=document.createElement("span")).className="gluon-remove",l.appendChild(o),v(o,"click",h(!1)),l.appendChild(document.createElement("br")))}(o=document.createElement("span")).className="gluon-add",l.appendChild(o),v(o,"click",h(!0))}function p(e){var t=(e=e||window.event).target||e.srcElement;switch(3==t.nodeType&&(t=t.parentNode),e.keyCode){case 8:case 46:return 0==t.value.length?(e.preventDefault&&e.preventDefault(),!1):!0;case 13:case 38:case 40:return e.preventDefault&&e.preventDefault(),!1}return!0}function f(e){var t,n,a,i=(e=e||window.event).target||e.srcElement,r=0;if(i){for(r=(i=3==i.nodeType?i.parentNode:i).index,t=i.previousSibling;t&&t.name!=s;)t=t.previousSibling;for(n=i.nextSibling;n&&n.name!=s;)n=n.nextSibling}switch(e.keyCode){case 8:case 46:if("select"==i.nodeName.toLowerCase()||0==i.value.length)return e.preventDefault&&e.preventDefault(),a=i.index,d(a=8==e.keyCode?1-a:a,-1,r),!1;break;case 13:d(-1,r,-1);break;case 38:t&&t.focus();break;case 40:n&&n.focus()}return!0}function h(n){return function(e){for(var t=((e=e||window.event).target||e.srcElement).previousSibling;t&&t.name!=s;)t=t.previousSibling;return n?f({target:t,keyCode:13}):(t.value="",f({target:t,keyCode:8})),!1}}d(NaN,-1,-1)}(b,w)}f()}();
\ No newline at end of file
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net> -- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0. -- Licensed to the public under the Apache License 2.0.
module('gluon.web.model', package.seeall)
local unistd = require 'posix.unistd' local unistd = require 'posix.unistd'
local classes = require 'gluon.web.model.classes' local classes = require 'gluon.web.model.classes'
...@@ -15,7 +13,7 @@ local function load(filename, i18n) ...@@ -15,7 +13,7 @@ local function load(filename, i18n)
local func = assert(loadfile(filename)) local func = assert(loadfile(filename))
setfenv(func, setmetatable({}, {__index = setfenv(func, setmetatable({}, {__index =
function(tbl, key) function(_, key)
return classes[key] or i18n[key] or _G[key] return classes[key] or i18n[key] or _G[key]
end end
})) }))
......
-- Copyright 2008 Steven Barth <steven@midlink.org> -- SPDX-License-Identifier: Apache-2.0
-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net> -- SPDX-FileCopyrightText: 2008, Steven Barth <steven@midlink.org>
-- Licensed to the public under the Apache License 2.0. -- SPDX-FileCopyrightText: 2017-2018, Matthias Schiffer <mschiffer@universe-factory.net>
-- SPDX-FileCopyrightText: 2023, Leonardo Mörlein <me@irrelefant.net>
module("gluon.web.model.classes", package.seeall)
local util = require "gluon.web.util" local util = require "gluon.web.util"
local gluon_util = require "gluon.util"
local datatypes = require "gluon.web.model.datatypes" local datatypes = require "gluon.web.model.datatypes"
local class = util.class local class = util.class
local instanceof = util.instanceof local instanceof = util.instanceof
FORM_NODATA = 0
FORM_VALID = 1 local M = {}
FORM_INVALID = -1
M.FORM_NODATA = 0
M.FORM_VALID = 1
M.FORM_INVALID = -1
local function parse_datatype(code) local function parse_datatype(code)
...@@ -41,15 +44,18 @@ local function verify_datatype(dt, value) ...@@ -41,15 +44,18 @@ local function verify_datatype(dt, value)
end end
Node = class() local Node = class()
M.Node = Node
function Node:__init__(title, description, name) function Node:__init__(name, title, description)
self.children = {} self.children = {}
self.deps = {}
self.title = title or "" self.title = title or ""
self.description = description or "" self.description = description or ""
self.name = name self.name = name
self.index = nil self.index = nil
self.parent = nil self.parent = nil
self.state = M.FORM_NODATA
self.package = 'gluon-web-model' self.package = 'gluon-web-model'
end end
...@@ -69,12 +75,33 @@ function Node:id() ...@@ -69,12 +75,33 @@ function Node:id()
return prefix.."."..self:id_suffix() return prefix.."."..self:id_suffix()
end end
function Node:reset_node()
self.state = M.FORM_NODATA
for _, child in ipairs(self.children) do
child:reset_node()
end
end
function Node:parse(http) function Node:parse(http)
self.state = M.FORM_VALID
for _, child in ipairs(self.children) do for _, child in ipairs(self.children) do
child:parse(http) child:parse(http)
end end
end end
function Node:propagate_state()
if self.state == M.FORM_NODATA then
return
end
for _, child in ipairs(self.children) do
child:propagate_state()
if child.state == M.FORM_INVALID then
self.state = M.FORM_INVALID
end
end
end
function Node:render(renderer, scope) function Node:render(renderer, scope)
if self.template then if self.template then
local env = setmetatable({ local env = setmetatable({
...@@ -92,104 +119,90 @@ function Node:render_children(renderer, scope) ...@@ -92,104 +119,90 @@ function Node:render_children(renderer, scope)
end end
end end
function Node:resolve_depends() function Node:depends(field, value)
local updated = false local deps
for _, node in ipairs(self.children) do if instanceof(field, Node) then
update = updated or node:resolve_depends() deps = { [field] = value }
end else
return updated deps = field
end end
function Node:handle() table.insert(self.deps, deps)
for _, node in ipairs(self.children) do
node:handle()
end
end end
function Node:deplist(deplist)
local deps = {}
Template = class(Node) for _, d in ipairs(deplist or self.deps) do
local a = {}
function Template:__init__(template) for k, v in pairs(d) do
Node.__init__(self) a[k:id()] = v
self.template = template end
table.insert(deps, a)
end end
if next(deps) then
return deps
end
end
Form = class(Node) function Node:resolve_depends()
local updated = self:resolve_node_depends()
function Form:__init__(...) for _, node in ipairs(self.children) do
Node.__init__(self, ...) updated = updated or node:resolve_depends()
self.template = "model/form"
end end
function Form:submitstate(http) return updated
return http:getenv("REQUEST_METHOD") == "POST" and http:formvalue(self:id()) ~= nil
end end
function Form:parse(http) function Node:resolve_node_depends()
if not self:submitstate(http) then if #self.deps == 0 then
self.state = FORM_NODATA return false
return
end end
Node.parse(self, http) for _, d in ipairs(self.deps) do
local valid = true
while self:resolve_depends() do end for k, v in pairs(d) do
if k.state ~= M.FORM_VALID or k.data ~= v then
for _, s in ipairs(self.children) do valid = false
for _, v in ipairs(s.children) do break
if v.state == FORM_INVALID then
self.state = FORM_INVALID
return
end end
end end
if valid then return false end
end end
self.state = FORM_VALID self:reset_node()
return true
end end
function Form:handle() -- will be overridden: write(value)
if self.state == FORM_VALID then function Node:write()
Node.handle(self)
self:write()
end
end end
function Form:write() function Node:handle()
if self.state == M.FORM_VALID then
for _, node in ipairs(self.children) do
node:handle()
end end
self:write(self.data)
function Form:section(t, ...)
assert(instanceof(t, Section), "class must be a descendent of Section")
local obj = t(...)
self:append(obj)
return obj
end end
Section = class(Node)
function Section:__init__(...)
Node.__init__(self, ...)
self.fields = {}
self.template = "model/section"
end end
function Section:option(t, option, title, description, ...)
assert(instanceof(t, AbstractValue), "class must be a descendant of AbstractValue")
local obj = t(title, description, option, ...) local Template = class(Node)
self:append(obj) M.Template = Template
self.fields[option] = obj
return obj
end
function Template:__init__(template)
Node.__init__(self)
self.template = template
end
AbstractValue = class(Node) local AbstractValue = class(Node)
M.AbstractValue = AbstractValue
function AbstractValue:__init__(option, ...) function AbstractValue:__init__(...)
Node.__init__(self, option, ...) Node.__init__(self, ...)
self.deps = {}
self.default = nil self.default = nil
self.size = nil self.size = nil
...@@ -197,34 +210,7 @@ function AbstractValue:__init__(option, ...) ...@@ -197,34 +210,7 @@ function AbstractValue:__init__(option, ...)
self.template = "model/valuewrapper" self.template = "model/valuewrapper"
self.state = FORM_NODATA self.error = false
end
function AbstractValue:depends(field, value)
local deps
if instanceof(field, Node) then
deps = { [field] = value }
else
deps = field
end
table.insert(self.deps, deps)
end
function AbstractValue:deplist(section, deplist)
local deps = {}
for _, d in ipairs(deplist or self.deps) do
local a = {}
for k, v in pairs(d) do
a[k:id()] = v
end
table.insert(deps, a)
end
if next(deps) then
return deps
end
end end
function AbstractValue:defaultvalue() function AbstractValue:defaultvalue()
...@@ -236,68 +222,38 @@ function AbstractValue:formvalue(http) ...@@ -236,68 +222,38 @@ function AbstractValue:formvalue(http)
end end
function AbstractValue:cfgvalue() function AbstractValue:cfgvalue()
if self.state == FORM_NODATA then if self.state == M.FORM_NODATA then
return self:defaultvalue() return self:defaultvalue()
else else
return self.data return self.data
end end
end end
function AbstractValue:add_error(type, msg) function AbstractValue:reset_node()
self.error = msg or type
if type == "invalid" then
self.tag_invalid = true
elseif type == "missing" then
self.tag_missing = true
end
self.state = FORM_INVALID
end
function AbstractValue:reset()
self.error = nil
self.tag_invalid = nil
self.tag_missing = nil
self.data = nil self.data = nil
self.state = FORM_NODATA self.error = false
self.state = M.FORM_NODATA
end end
function AbstractValue:parse(http) function AbstractValue:parse(http)
self.data = self:formvalue(http) self.data = self:formvalue(http)
local ok, err = self:validate() if not self:validate() then
if not ok then self.error = true
if type(self.data) ~= "string" or #self.data > 0 then self.state = M.FORM_INVALID
self:add_error("invalid", err)
else
self:add_error("missing", err)
end
return return
end end
self.state = FORM_VALID self.state = M.FORM_VALID
end end
function AbstractValue:resolve_depends() function AbstractValue:resolve_node_depends()
if self.state == FORM_NODATA or #self.deps == 0 then if self.state == M.FORM_NODATA then
return false return false
end end
for _, d in ipairs(self.deps) do return Node.resolve_node_depends(self)
local valid = true
for k, v in pairs(d) do
if k.state ~= FORM_VALID or k.data ~= v then
valid = false
break
end
end
if valid then return false end
end
self:reset()
return true
end end
function AbstractValue:validate() function AbstractValue:validate()
...@@ -317,17 +273,9 @@ function AbstractValue:validate() ...@@ -317,17 +273,9 @@ function AbstractValue:validate()
end end
function AbstractValue:handle()
if self.state == FORM_VALID then
self:write(self.data)
end
end
function AbstractValue:write(value) local Value = class(AbstractValue)
end M.Value = Value
Value = class(AbstractValue)
function Value:__init__(...) function Value:__init__(...)
AbstractValue.__init__(self, ...) AbstractValue.__init__(self, ...)
...@@ -335,7 +283,8 @@ function Value:__init__(...) ...@@ -335,7 +283,8 @@ function Value:__init__(...)
end end
Flag = class(AbstractValue) local Flag = class(AbstractValue)
M.Flag = Flag
function Flag:__init__(...) function Flag:__init__(...)
AbstractValue.__init__(self, ...) AbstractValue.__init__(self, ...)
...@@ -353,7 +302,8 @@ function Flag:validate() ...@@ -353,7 +302,8 @@ function Flag:validate()
end end
ListValue = class(AbstractValue) local ListValue = class(AbstractValue)
M.ListValue = ListValue
function ListValue:__init__(...) function ListValue:__init__(...)
AbstractValue.__init__(self, ...) AbstractValue.__init__(self, ...)
...@@ -413,7 +363,85 @@ function ListValue:validate() ...@@ -413,7 +363,85 @@ function ListValue:validate()
end end
DynamicList = class(AbstractValue) 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
function DynamicList:__init__(...) function DynamicList:__init__(...)
AbstractValue.__init__(self, ...) AbstractValue.__init__(self, ...)
...@@ -452,9 +480,105 @@ function DynamicList:validate() ...@@ -452,9 +480,105 @@ function DynamicList:validate()
end end
TextValue = class(AbstractValue) local TextValue = class(AbstractValue)
M.TextValue = TextValue
function TextValue:__init__(...) function TextValue:__init__(...)
AbstractValue.__init__(self, ...) AbstractValue.__init__(self, ...)
self.subtemplate = "model/tvalue" self.subtemplate = "model/tvalue"
end end
local Element = class(AbstractValue)
M.Element = Element
function Element:__init__(template, kv, ...)
AbstractValue.__init__(self, ...)
self.default = nil
self.size = nil
self.optional = false
self.template = template
for key, value in pairs(kv) do
self[key] = value
end
self.error = false
end
function Element:parse(http)
if not self.datatype then
self.state = M.FORM_VALID
return
end
return AbstractValue:parse(http)
end
function Element:validate()
if not self.datatype then
return true
end
AbstractValue:validate()
end
local Section = class(Node)
M.Section = Section
function Section:__init__(title, description, name)
Node.__init__(self, name, title, description)
self.template = "model/section"
end
function Section:option(t, ...)
assert(instanceof(t, AbstractValue), "class must be a descendant of AbstractValue")
local obj = t(...)
self:append(obj)
return obj
end
function Section:element(...)
local obj = Element(...)
self:append(obj)
return obj
end
local Form = class(Node)
M.Form = Form
function Form:__init__(title, description, name)
Node.__init__(self, name, title, description)
self.template = "model/form"
end
function Form:submitstate(http)
return http:getenv("REQUEST_METHOD") == "POST" and http:formvalue(self:id()) ~= nil
end
function Form:parse(http)
if not self:submitstate(http) then
self.state = M.FORM_NODATA
return
end
Node.parse(self, http)
while self:resolve_depends() do end
self:propagate_state()
end
function Form:section(t, ...)
assert(instanceof(t, Section), "class must be a descendent of Section")
local obj = t(...)
self:append(obj)
return obj
end
return M
-- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org> -- SPDX-License-Identifier: Apache-2.0
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net> -- SPDX-FileCopyrightText: 2010, Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0. -- SPDX-FileCopyrightText: 2017, Matthias Schiffer <mschiffer@universe-factory.net>
local tonumber = tonumber local M = {}
function M.bool(val)
module "gluon.web.model.datatypes"
function bool(val)
if val == "1" or val == "yes" or val == "on" or val == "true" then if val == "1" or val == "yes" or val == "on" or val == "true" then
return true return true
elseif val == "0" or val == "no" or val == "off" or val == "false" then elseif val == "0" or val == "no" or val == "off" or val == "false" then
...@@ -32,29 +28,29 @@ local function int(val) ...@@ -32,29 +28,29 @@ local function int(val)
end end
end end
function uinteger(val) function M.uinteger(val)
local n = int(val) local n = int(val)
return (n ~= nil and n >= 0) return (n ~= nil and n >= 0)
end end
function integer(val) function M.integer(val)
return (int(val) ~= nil) return (int(val) ~= nil)
end end
function ufloat(val) function M.ufloat(val)
local n = dec(val) local n = dec(val)
return (n ~= nil and n >= 0) return (n ~= nil and n >= 0)
end end
function float(val) function M.float(val)
return (dec(val) ~= nil) return (dec(val) ~= nil)
end end
function ipaddr(val) function M.ipaddr(val)
return ip4addr(val) or ip6addr(val) return M.ip4addr(val) or M.ip6addr(val)
end end
function ip4addr(val) function M.ip4addr(val)
local g = '(%d%d?%d?)' local g = '(%d%d?%d?)'
local v1, v2, v3, v4 = val:match('^'..((g..'%.'):rep(3))..g..'$') local v1, v2, v3, v4 = val:match('^'..((g..'%.'):rep(3))..g..'$')
local n1, n2, n3, n4 = tonumber(v1), tonumber(v2), tonumber(v3), tonumber(v4) local n1, n2, n3, n4 = tonumber(v1), tonumber(v2), tonumber(v3), tonumber(v4)
...@@ -69,7 +65,7 @@ function ip4addr(val) ...@@ -69,7 +65,7 @@ function ip4addr(val)
) )
end end
function ip6addr(val) function M.ip6addr(val)
local g1 = '%x%x?%x?%x?' local g1 = '%x%x?%x?%x?'
if not val:match('::') then if not val:match('::') then
...@@ -100,7 +96,7 @@ function ip6addr(val) ...@@ -100,7 +96,7 @@ function ip6addr(val)
return false return false
end end
function wpakey(val) function M.wpakey(val)
if #val == 64 then if #val == 64 then
return (val:match("^%x+$") ~= nil) return (val:match("^%x+$") ~= nil)
else else
...@@ -108,11 +104,11 @@ function wpakey(val) ...@@ -108,11 +104,11 @@ function wpakey(val)
end end
end end
function range(val, vmin, vmax) function M.range(val, vmin, vmax)
return min(val, vmin) and max(val, vmax) return M.min(val, vmin) and M.max(val, vmax)
end end
function min(val, min) function M.min(val, min)
val = dec(val) val = dec(val)
min = tonumber(min) min = tonumber(min)
...@@ -123,7 +119,7 @@ function min(val, min) ...@@ -123,7 +119,7 @@ function min(val, min)
return false return false
end end
function max(val, max) function M.max(val, max)
val = dec(val) val = dec(val)
max = tonumber(max) max = tonumber(max)
...@@ -134,19 +130,19 @@ function max(val, max) ...@@ -134,19 +130,19 @@ function max(val, max)
return false return false
end end
function irange(val, vmin, vmax) function M.irange(val, vmin, vmax)
return integer(val) and range(val, vmin, vmax) return M.integer(val) and M.range(val, vmin, vmax)
end end
function imin(val, vmin) function M.imin(val, vmin)
return integer(val) and min(val, vmin) return M.integer(val) and M.min(val, vmin)
end end
function imax(val, vmax) function M.imax(val, vmax)
return integer(val) and max(val, vmax) return M.integer(val) and M.max(val, vmax)
end end
function minlength(val, min) function M.minlength(val, min)
min = tonumber(min) min = tonumber(min)
if min ~= nil then if min ~= nil then
...@@ -156,7 +152,7 @@ function minlength(val, min) ...@@ -156,7 +152,7 @@ function minlength(val, min)
return false return false
end end
function maxlength(val, max) function M.maxlength(val, max)
max = tonumber(max) max = tonumber(max)
if max ~= nil then if max ~= nil then
...@@ -165,3 +161,5 @@ function maxlength(val, max) ...@@ -165,3 +161,5 @@ function maxlength(val, max)
return false return false
end end
return M
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-web-network PKG_NAME:=gluon-web-network
PKG_VERSION:=1
PKG_RELEASE:=1
include ../gluon.mk include ../gluon.mk
......
...@@ -19,18 +19,15 @@ msgstr "Automatisch (RA/DHCPv6)" ...@@ -19,18 +19,15 @@ msgstr "Automatisch (RA/DHCPv6)"
msgid "Disabled" msgid "Disabled"
msgstr "Deaktiviert" msgstr "Deaktiviert"
msgid "Enable \"%s\""
msgstr "\"%s\" aktivieren"
msgid "Enable PoE Passthrough" msgid "Enable PoE Passthrough"
msgstr "PoE-Passthrough aktivieren" msgstr "PoE-Passthrough aktivieren"
msgid "Enable PoE Power Port %s" msgid "Enable PoE Power Port %s"
msgstr "PoE-Ausgabe auf Port %s aktivieren" msgstr "PoE-Ausgabe auf Port %s 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" msgid "Gateway"
msgstr "Gateway" msgstr "Gateway"
...@@ -43,6 +40,12 @@ msgstr "IPv4" ...@@ -43,6 +40,12 @@ msgstr "IPv4"
msgid "IPv6" msgid "IPv6"
msgstr "IPv6" msgstr "IPv6"
msgid "Interface"
msgstr "Interface"
msgid "LAN Interfaces"
msgstr "LAN-Interfaces"
msgid "Netmask" msgid "Netmask"
msgstr "Netzmaske" msgstr "Netzmaske"
...@@ -55,5 +58,8 @@ msgstr "Statisch" ...@@ -55,5 +58,8 @@ msgstr "Statisch"
msgid "Static DNS servers" msgid "Static DNS servers"
msgstr "Statische DNS-Server" msgstr "Statische DNS-Server"
msgid "WAN Interfaces"
msgstr "WAN-Interfaces"
msgid "WAN connection" msgid "WAN connection"
msgstr "WAN-Verbindung" msgstr "WAN-Verbindung"
...@@ -19,18 +19,15 @@ msgstr "Automatique (RA/DHCPv6)" ...@@ -19,18 +19,15 @@ msgstr "Automatique (RA/DHCPv6)"
msgid "Disabled" msgid "Disabled"
msgstr "Désactivé" msgstr "Désactivé"
msgid "Enable \"%s\""
msgstr ""
msgid "Enable PoE Passthrough" msgid "Enable PoE Passthrough"
msgstr "" msgstr ""
msgid "Enable PoE Power Port %s" msgid "Enable PoE Power Port %s"
msgstr "" 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" msgid "Gateway"
msgstr "Passerelle" msgstr "Passerelle"
......
...@@ -10,16 +10,13 @@ msgstr "" ...@@ -10,16 +10,13 @@ msgstr ""
msgid "Disabled" msgid "Disabled"
msgstr "" msgstr ""
msgid "Enable PoE Passthrough" msgid "Enable \"%s\""
msgstr ""
msgid "Enable PoE Power Port %s"
msgstr "" msgstr ""
msgid "Enable meshing on the LAN interface" msgid "Enable PoE Passthrough"
msgstr "" msgstr ""
msgid "Enable meshing on the WAN interface" msgid "Enable PoE Power Port %s"
msgstr "" msgstr ""
msgid "Gateway" msgid "Gateway"
...@@ -34,6 +31,12 @@ msgstr "" ...@@ -34,6 +31,12 @@ msgstr ""
msgid "IPv6" msgid "IPv6"
msgstr "" msgstr ""
msgid "Interface"
msgstr ""
msgid "LAN Interfaces"
msgstr ""
msgid "Netmask" msgid "Netmask"
msgstr "" msgstr ""
...@@ -46,5 +49,8 @@ msgstr "" ...@@ -46,5 +49,8 @@ msgstr ""
msgid "Static DNS servers" msgid "Static DNS servers"
msgstr "" msgstr ""
msgid "WAN Interfaces"
msgstr ""
msgid "WAN connection" msgid "WAN connection"
msgstr "" msgstr ""
--[[ -- SPDX-License-Identifier: Apache-2.0
Copyright 2014 Nils Schneider <nils@nilsschneider.net> -- SPDX-FileCopyrightText: 2014, Nils Schneider <nils@nilsschneider.net>
-- SPDX-FileCopyrightText: 2023, Leonardo Mörlein <me@irrelefant.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
]]--
local uci = require("simple-uci").cursor() local uci = require("simple-uci").cursor()
local sysconfig = require 'gluon.sysconfig'
local util = require 'gluon.util'
local wan = uci:get_all("network", "wan") local wan = uci:get_all("network", "wan")
local wan6 = uci:get_all("network", "wan6") local wan6 = uci:get_all("network", "wan6")
local dns_static = uci:get_first("gluon-wan-dnsmasq", "static") local dns_static = uci:get_first("gluon-wan-dnsmasq", "static")
local f = Form(translate("WAN connection")) local f = Form(translate("WAN connection"))
local s = f:section(Section) local s = f:section(Section)
...@@ -42,7 +35,7 @@ ipv4_gateway.default = wan.gateway ...@@ -42,7 +35,7 @@ ipv4_gateway.default = wan.gateway
ipv4_gateway.datatype = "ip4addr" ipv4_gateway.datatype = "ip4addr"
local s = f:section(Section) s = f:section(Section)
local ipv6 = s:option(ListValue, "ipv6", translate("IPv6")) local ipv6 = s:option(ListValue, "ipv6", translate("IPv6"))
ipv6:value("dhcpv6", translate("Automatic (RA/DHCPv6)")) ipv6:value("dhcpv6", translate("Automatic (RA/DHCPv6)"))
...@@ -61,7 +54,7 @@ ipv6_gateway.default = wan6.ip6gw ...@@ -61,7 +54,7 @@ ipv6_gateway.default = wan6.ip6gw
ipv6_gateway.datatype = "ip6addr" ipv6_gateway.datatype = "ip6addr"
if dns_static then if dns_static then
local s = f:section(Section) s = f:section(Section)
local dns = s:option(DynamicList, "dns", translate("Static DNS servers")) local dns = s:option(DynamicList, "dns", translate("Static DNS servers"))
dns.default = uci:get_list("gluon-wan-dnsmasq", dns_static, "server") dns.default = uci:get_list("gluon-wan-dnsmasq", dns_static, "server")
...@@ -74,57 +67,83 @@ if dns_static then ...@@ -74,57 +67,83 @@ if dns_static then
end end
end end
local s = f:section(Section) s = f:section(Section)
local mesh_wan = s:option(Flag, "mesh_wan", translate("Enable meshing on the WAN interface")) local pretty_ifnames = {
mesh_wan.default = not uci:get_bool("network", "mesh_wan", "disabled") ["/wan"] = translate("WAN Interfaces"),
["/single"] = translate("Interface"),
["/lan"] = translate("LAN Interfaces")
}
function mesh_wan:write(data) uci:foreach('gluon', 'interface', function(config)
uci:set("network", "mesh_wan", "disabled", not data) local section_name = config['.name']
end local ifaces = s:option(MultiListValue, section_name, pretty_ifnames[config.name] or config.name)
if sysconfig.lan_ifname then ifaces.orientation = 'horizontal'
local s = f:section(Section) ifaces:value('uplink', 'Uplink')
ifaces:value('mesh', 'Mesh')
local mesh_lan = s:option(Flag, "mesh_lan", translate("Enable meshing on the LAN interface")) ifaces:value('client', 'Client')
mesh_lan.default = not uci:get_bool("network", "mesh_lan", "disabled") ifaces:exclusive('uplink', 'client')
ifaces:exclusive('mesh', 'client')
function mesh_lan:write(data)
uci:set("network", "mesh_lan", "disabled", not data)
local interfaces = uci:get_list("network", "client", "ifname") ifaces.default = config.role
for lanif in sysconfig.lan_ifname:gmatch('%S+') do function ifaces:write(data)
if data then uci:set_list("gluon", section_name, "role", data)
util.remove_from_set(interfaces, lanif)
else
util.add_to_set(interfaces, lanif)
end
end end
end)
uci:set_list("network", "client", "ifname", interfaces)
end
end
local section local section
uci:foreach("system", "gpio_switch", function(s) uci:foreach("system", "gpio_switch", function(si)
if s[".name"]:match("poe") then if si[".name"]:match("poe") then
if not section then if not section then
section = f:section(Section) section = f:section(Section)
end end
local port = s.name:match("^PoE Power Port(%d*)$") local texts = {
["^PoE Power Port(%d*)$"] = {
get_title = function(m) return translatef("Enable PoE Power Port %s", m[1]) end,
active_low = false,
},
["^PoE Passthrough$"] = {
get_title = function() return translate("Enable PoE Passthrough") end,
active_low = false,
},
-- Typo in AP-303H (ipq40xx-generic)
["^POE passtrough disable$"] = {
get_title = function() return translate("Enable PoE Passthrough") end,
active_low = true,
},
}
local name local name
if port then local active_low = false
name = translatef("Enable PoE Power Port %s", port) for pattern, text_obj in pairs(texts) do
else local match = {si.name:match(pattern)}
name = translate("Enable " .. s.name) if match[1] then
name = text_obj.get_title(match)
active_low = text_obj.active_low
break
end
end
if not name then
name = translatef('Enable "%s"', si.name)
end
local poe = section:option(Flag, si[".name"], name)
poe.default = uci:get_bool("system", si[".name"], "value")
if active_low then
poe.default = not poe.default
end end
local poe = section:option(Flag, s[".name"], name)
poe.default = uci:get_bool("system", s[".name"], "value")
function poe:write(data) function poe:write(data)
uci:set("system", s[".name"], "value", data) local write_value = data
if active_low then
write_value = not write_value
end
uci:set("system", si[".name"], "value", write_value)
end end
end end
end) end)
...@@ -150,9 +169,10 @@ function f:write() ...@@ -150,9 +169,10 @@ function f:write()
uci:delete("network", "wan6", "ip6gw") uci:delete("network", "wan6", "ip6gw")
end end
uci:commit('gluon')
uci:commit("network") uci:commit("network")
uci:commit('system') uci:commit('system')
end end
return f return f
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-web-node-role PKG_NAME:=gluon-web-node-role
PKG_VERSION:=1
include ../gluon.mk include ../gluon.mk
......
...@@ -10,18 +10,18 @@ msgstr "" ...@@ -10,18 +10,18 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Node role"
msgstr "Verwendungszweck"
msgid "Role"
msgstr "Rolle"
msgid "" msgid ""
"If this node has a special role within the mesh network you can specify this " "If this node has a special role within the mesh network you can specify this "
"role here. Please find out about the available roles and their impact first. " "role here. Please find out about the available roles and their impact first. "
"Only change the role if you know what you are doing." "Only change the role if you know what you are doing."
msgstr "" msgstr ""
"Wenn dein Knoten eine besondere Rolle im Mesh-Netzwerk einnimmt, " "Wenn dein Knoten eine besondere Rolle im Mesh-Netzwerk einnimmt, kannst du "
"kannst du diese hier angeben. Bringe bitte zuvor in Erfahrung, welche " "diese hier angeben. Bringe bitte zuvor in Erfahrung, welche Bedeutung die "
"Bedeutung die zur Verfügung stehenden Rollen haben. " "zur Verfügung stehenden Rollen haben. Setze die Rolle nur, wenn du weißt, "
"Setze die Rolle nur, wenn du weißt, was du tust." "was du tust."
msgid "Node role"
msgstr "Verwendungszweck"
msgid "Role"
msgstr "Rolle"
...@@ -10,18 +10,18 @@ msgstr "" ...@@ -10,18 +10,18 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Node role"
msgstr "Rôle du nœud"
msgid "Role"
msgstr "Rôle"
msgid "" msgid ""
"If this node has a special role within the mesh network you can specify this " "If this node has a special role within the mesh network you can specify this "
"role here. Please find out about the available roles and their impact first. " "role here. Please find out about the available roles and their impact first. "
"Only change the role if you know what you are doing." "Only change the role if you know what you are doing."
msgstr "" msgstr ""
"Si votre nœud a un rôle spécial dans le réseau MESH, vous pouvez " "Si votre nœud a un rôle spécial dans le réseau MESH, vous pouvez spécifier "
"spécifier ce rôle ici. Avant de changer, informez vous sur les rôles " "ce rôle ici. Avant de changer, informez vous sur les rôles disponibles et "
"disponibles et sur leur impacts. Changez de rôle uniquement si vous " "sur leur impacts. Changez de rôle uniquement si vous comprenez ce que vous "
"comprenez ce que vous faites." "faites."
msgid "Node role"
msgstr "Rôle du nœud"
msgid "Role"
msgstr "Rôle"
msgid "" msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8" msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Node role" msgid ""
"If this node has a special role within the mesh network you can specify this "
"role here. Please find out about the available roles and their impact first. "
"Only change the role if you know what you are doing."
msgstr "" msgstr ""
msgid "Role" msgid "Node role"
msgstr "" msgstr ""
msgid "" msgid "Role"
"If this node has a special role within the mesh network you can specify this role here. "
"Please find out about the available roles and their impact first. "
"Only change the role if you know what you are doing."
msgstr "" msgstr ""
local f, s, o local f, s, o
local site = require 'gluon.site' local site = require 'gluon.site'
local site_i18n = i18n 'gluon-site'
local uci = require("simple-uci").cursor() local uci = require("simple-uci").cursor()
local config = 'gluon-node-info' local config = 'gluon-node-info'
...@@ -9,15 +10,15 @@ local role = uci:get(config, uci:get_first(config, "system"), "role") ...@@ -9,15 +10,15 @@ local role = uci:get(config, uci:get_first(config, "system"), "role")
f = Form(translate("Node role")) f = Form(translate("Node role"))
s = f:section(Section, nil, translate( s = f:section(Section, nil, translate(
"If this node has a special role within the freifunk network you can specify this role here. " "If this node has a special role within the mesh network you can specify this role here. "
.. "Please find out about the available roles and their impact first. " .. "Please find out about the available roles and their impact first. "
.. "Only change the role if you know what you are doing." .. "Only change the role if you know what you are doing."
)) ))
o = s:option(ListValue, "role", translate("Role")) o = s:option(ListValue, "role", translate("Role"))
o.default = role o.default = role
for _, role in ipairs(site.roles.list()) do for _, role_value in ipairs(site.roles.list()) do
o:value(role, translate('gluon-web-node-role:role:' .. role)) o:value(role_value, site_i18n.translate('gluon-web-node-role:role:' .. role_value))
end end
function o:write(data) function o:write(data)
......
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-web-osm PKG_NAME:=gluon-web-osm
PKG_VERSION:=1
include ../gluon.mk include ../gluon.mk
...@@ -9,4 +8,17 @@ define Package/gluon-web-osm ...@@ -9,4 +8,17 @@ define Package/gluon-web-osm
TITLE:=base src for OSM inlay TITLE:=base src for OSM inlay
endef endef
PKG_CONFIG_DEPENDS += CONFIG_GLUON_MINIFY
define Package/gluon-web-osm/install
$(Gluon/Build/Install)
$(INSTALL_DIR) $(1)/lib/gluon/web/www/static/
ifdef CONFIG_GLUON_MINIFY
$(INSTALL_DATA) ./javascript/gluon-web-osm.min.js $(1)/lib/gluon/web/www/static/gluon-web-osm.js
else
$(INSTALL_DATA) ./javascript/gluon-web-osm.js $(1)/lib/gluon/web/www/static/
endif
endef
$(eval $(call BuildPackageGluon,gluon-web-osm)) $(eval $(call BuildPackageGluon,gluon-web-osm))
<div id="<%=id%>" class="gluon-osm-map" style="display: none"></div>
<script type="text/javascript" src="/static/gluon-web-osm.js"></script>
<script type="text/javascript">
(function() {
var elMap = document.getElementById(<%=json(id)%>);
var wrapper = elMap.parentNode;
var elLon, elLat;
window.addEventListener('gluon-update', function() {
<% if self.lon then -%>
elLon = document.getElementById(<%=json(self.lon:id())%>);
<%- end %>
<% if self.lat then -%>
elLat = document.getElementById(<%=json(self.lat:id())%>);
<%- end %>
}, {once: true});
initOSM(<%=json(self.options)%>, function(createMap) {
elMap.style.display = '';
var pos = <%=json(self:cfgvalue().pos)%>;
var map = createMap(
elMap,
[pos.lon, pos.lat],
<%=json(self:cfgvalue().zoom)%>,
<%=json(self:cfgvalue().set)%>,
function(lonlat) {
if (elLon) {
elLon.value = lonlat[0].toFixed(6);
elLon.dispatchEvent(new Event('gluon-revalidate'));
}
if (elLat) {
elLat.value = lonlat[1].toFixed(6);
elLat.dispatchEvent(new Event('gluon-revalidate'));
}
}
);
wrapper.addEventListener('gluon-show', function() {
map.updateSize();
});
});
})();
</script>
function findObj(e){for(list=document.getElementsByClassName("gluon-input-text"),i=0;i<list.length;i++)if(item=list.item(i),0<=item.id.indexOf(e))return item;return!1}function showMap(){if("object"==typeof OpenLayers&&!1!==findObj("longitude")){document.getElementById("locationPickerMap").style.display="block";var a=new OpenLayers.Projection("EPSG:4326"),o=new OpenLayers.Projection("EPSG:900913"),e=zoom,t=new OpenLayers.Layer.Markers("Markers");OpenLayers.Control.Click=OpenLayers.Class(OpenLayers.Control,{defaultHandlerOptions:{single:!0,double:!1,pixelTolerance:0,stopSingle:!1,stopDouble:!1},initialize:function(){this.handlerOptions=OpenLayers.Util.extend({},this.defaultHandlerOptions),OpenLayers.Control.prototype.initialize.apply(this,arguments),this.handler=new OpenLayers.Handler.Click(this,{click:this.trigger},this.handlerOptions)},trigger:function(e){var n=osmMap.getLonLatFromPixel(e.xy);oLon=findObj("longitude"),oLat=findObj("latitude"),lonlat1=new OpenLayers.LonLat(n.lon,n.lat).transform(o,a),oLon.value=lonlat1.lon,oLat.value=lonlat1.lat,t.clearMarkers(),t.addMarker(new OpenLayers.Marker(n)),oLon.className=oLon.className.replace(/ gluon-input-invalid/g,""),oLat.className=oLat.className.replace(/ gluon-input-invalid/g,"")}}),osmMap=new OpenLayers.Map("locationPickerMap",{controls:[new OpenLayers.Control.Navigation,new OpenLayers.Control.PanZoomBar,new OpenLayers.Control.MousePosition],maxExtent:new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),numZoomLevels:18,maxResolution:156543,units:"m",projection:o,displayProjection:a});var n=new OpenLayers.Layer.OSM("OpenStreetMap");osmMap.addLayer(n),osmMap.addLayer(t);var r=longitude,i=latitude;oLon=findObj("longitude"),oLat=findObj("latitude"),""!=oLon.value&&(r=oLon.value),""!=oLat.value&&(i=oLat.value),t.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(r,i).transform(a,o)));var l=new OpenLayers.LonLat(r,i).transform(a,o);osmMap.setCenter(l,e);var s=new OpenLayers.Control.Click;osmMap.addControl(s),s.activate()}else setTimeout(showMap,1e3)}
\ No newline at end of file
/*
Build using:
uglifyjs javascript/gluon-web-osm.js -o javascript/gluon-web-osm.min.js -c -m
*/
'use strict';
function initOSM(options, ready) {
var markerSvg = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="30" height="45">'
+ '<path d="M2,15A13,13,0,0,1,28,13Q28,28,15,45Q2,28,2,15" fill="#48b" stroke="#369" stroke-width="1.5" />'
+ '<circle cx="15" cy="15" r="6" fill="#fff" />'
+ '</svg>';
var style = document.createElement('link');
style.rel = 'stylesheet';
style.type = 'text/css';
style.href = options.openlayers_url + '/css/ol.css';
document.head.appendChild(style);
var script = document.createElement('script');
var done = false;
script.onload = script.onreadystatechange = function() {
if (done)
return;
if (this.readyState && this.readyState !== "loaded" && this.readyState !== "complete")
return;
done = true;
var markerImg = new Image();
markerImg.onload = function() {
var markerStyle = new ol.style.Style({
image: new ol.style.Icon({
img: markerImg,
imgSize: [30, 45],
anchor: [0.5, 1]
})
});
var marker = new ol.Feature();
marker.setStyle(markerStyle);
var source;
if (options.tile_layer && options.tile_layer.type === 'XYZ') {
source = new ol.source.XYZ({
url: options.tile_layer.url,
attributions: options.tile_layer.attributions,
});
} else {
source = new ol.source.OSM();
}
ready(function(elMap, pos, zoom, set, onUpdate) {
var map = new ol.Map({
target: elMap,
layers: [
new ol.layer.Tile({
source: source
}),
new ol.layer.Vector({
source: new ol.source.Vector({
features: [marker]
})
})
],
view: new ol.View({
center: ol.proj.fromLonLat(pos),
zoom: zoom,
})
});
var refresh = function(coord) {
marker.setGeometry(new ol.geom.Point(coord));
}
map.addEventListener('click', function(e) {
refresh(e.coordinate);
onUpdate(ol.proj.toLonLat(e.coordinate));
});
if (set)
refresh(ol.proj.fromLonLat(pos));
return map;
});
}
markerImg.src = 'data:image/svg+xml,' + escape(markerSvg);
};
script.src = options.openlayers_url + '/build/ol.js';
document.head.appendChild(script);
}