diff options
Diffstat (limited to 'modules/luci-base/luasrc')
47 files changed, 578 insertions, 1183 deletions
diff --git a/modules/luci-base/luasrc/cbi.lua b/modules/luci-base/luasrc/cbi.lua index d275c5b275..971830fe81 100644 --- a/modules/luci-base/luasrc/cbi.lua +++ b/modules/luci-base/luasrc/cbi.lua @@ -1199,19 +1199,20 @@ function TypedSection.parse(self, novld) if name then -- Ignore if it already exists if self:cfgvalue(name) then - name = nil; - end - - name = self:checkscope(name) - - if not name then + name = nil self.err_invalid = true - end + else + name = self:checkscope(name) + + if not name then + self.err_invalid = true + end - if name and #name > 0 then - created = self:create(name, origin) and name - if not created then - self.invalid_cts = true + if name and #name > 0 then + created = self:create(name, origin) and name + if not created then + self.invalid_cts = true + end end end end diff --git a/modules/luci-base/luasrc/cbi/datatypes.lua b/modules/luci-base/luasrc/cbi/datatypes.lua index 99113e0b7a..33d018d2d0 100644 --- a/modules/luci-base/luasrc/cbi/datatypes.lua +++ b/modules/luci-base/luasrc/cbi/datatypes.lua @@ -132,6 +132,10 @@ function ip6prefix(val) return ( val and val >= 0 and val <= 128 ) end +function cidr(val) + return cidr4(val) or cidr6(val) +end + function cidr4(val) local ip, mask = val:match("^([^/]+)/([^/]+)$") @@ -460,3 +464,7 @@ function dateyyyymmdd(val) end return false end + +function unique(val) + return true +end diff --git a/modules/luci-base/luasrc/controller/admin/index.lua b/modules/luci-base/luasrc/controller/admin/index.lua index 7e0a207437..1f7db0cb38 100644 --- a/modules/luci-base/luasrc/controller/admin/index.lua +++ b/modules/luci-base/luasrc/controller/admin/index.lua @@ -80,8 +80,14 @@ function index() if has_wifi then page = entry({"admin", "wireless_assoclist"}, call("wifi_assoclist"), nil) page.leaf = true + + page = entry({"admin", "wireless_deauth"}, post("wifi_deauth"), nil) + page.leaf = true end + page = entry({"admin", "translations"}, call("action_translations"), nil) + page.leaf = true + -- Logout is last entry({"admin", "logout"}, call("action_logout"), _("Logout"), 999) end @@ -94,14 +100,35 @@ function action_logout() if sid then utl.ubus("session", "destroy", { ubus_rpc_session = sid }) - luci.http.header("Set-Cookie", "sysauth=%s; expires=%s; path=%s/" %{ - sid, 'Thu, 01 Jan 1970 01:00:00 GMT', dsp.build_url() + luci.http.header("Set-Cookie", "sysauth=%s; expires=%s; path=%s" %{ + '', 'Thu, 01 Jan 1970 01:00:00 GMT', dsp.build_url() }) end luci.http.redirect(dsp.build_url()) end +function action_translations(lang) + local i18n = require "luci.i18n" + local http = require "luci.http" + local fs = require "nixio".fs + + if lang and #lang > 0 then + lang = i18n.setlanguage(lang) + if lang then + local s = fs.stat("%s/base.%s.lmo" %{ i18n.i18ndir, lang }) + if s then + http.header("Cache-Control", "public, max-age=31536000") + http.header("ETag", "%x-%x-%x" %{ s["ino"], s["size"], s["mtime"] }) + end + end + end + + http.prepare_content("application/javascript; charset=utf-8") + http.write("window.TR=") + http.write_json(i18n.dump()) +end + function lease_status() local s = require "luci.tools.status" @@ -120,3 +147,18 @@ function wifi_assoclist() luci.http.prepare_content("application/json") luci.http.write_json(s.wifi_assoclist()) end + +function wifi_deauth() + local iface = luci.http.formvalue("iface") + local bssid = luci.http.formvalue("bssid") + + if iface and bssid then + luci.util.ubus("hostapd.%s" % iface, "del_client", { + addr = bssid, + deauth = true, + reason = 5, + ban_time = 60000 + }) + end + luci.http.status(200, "OK") +end diff --git a/modules/luci-base/luasrc/dispatcher.lua b/modules/luci-base/luasrc/dispatcher.lua index 8b9003d207..626a46dfd2 100644 --- a/modules/luci-base/luasrc/dispatcher.lua +++ b/modules/luci-base/luasrc/dispatcher.lua @@ -40,6 +40,28 @@ function build_url(...) return table.concat(url, "") end +function _ordered_children(node) + local name, child, children = nil, nil, {} + + for name, child in pairs(node.nodes) do + children[#children+1] = { + name = name, + node = child, + order = child.order or 100 + } + end + + table.sort(children, function(a, b) + if a.order == b.order then + return a.name < b.name + else + return a.order < b.order + end + end) + + return children +end + function node_visible(node) if node then return not ( @@ -55,15 +77,10 @@ end function node_childs(node) local rv = { } if node then - local k, v - for k, v in util.spairs(node.nodes, - function(a, b) - return (node.nodes[a].order or 100) - < (node.nodes[b].order or 100) - end) - do - if node_visible(v) then - rv[#rv+1] = k + local _, child + for _, child in ipairs(_ordered_children(node)) do + if node_visible(child.node) then + rv[#rv+1] = child.name end end end @@ -296,10 +313,6 @@ function dispatch(request) ctx.requestpath = ctx.requestpath or freq ctx.path = preq - if track.i18n then - i18n.loadc(track.i18n) - end - -- Init template engine if (c and c.index) or not track.notemplate then local tpl = require("luci.template") @@ -315,7 +328,7 @@ function dispatch(request) assert(media, "No valid theme found") end - local function _ifattr(cond, key, val) + local function _ifattr(cond, key, val, noescape) if cond then local env = getfenv(3) local scope = (type(env.self) == "table") and env.self @@ -326,13 +339,16 @@ function dispatch(request) val = util.serialize_json(val) end end - return string.format( - ' %s="%s"', tostring(key), - util.pcdata(tostring( val - or (type(env[key]) ~= "function" and env[key]) - or (scope and type(scope[key]) ~= "function" and scope[key]) - or "" )) - ) + + val = tostring(val or + (type(env[key]) ~= "function" and env[key]) or + (scope and type(scope[key]) ~= "function" and scope[key]) or "") + + if noescape ~= true then + val = util.pcdata(val) + end + + return string.format(' %s="%s"', tostring(key), val) else return '' end @@ -421,6 +437,7 @@ function dispatch(request) context.path = {} http.status(403, "Forbidden") + http.header("X-LuCI-Login-Required", "yes") tmpl.render(track.sysauth_template or "sysauth", { duser = default_user, fuser = user @@ -437,6 +454,7 @@ function dispatch(request) if not sid or not sdat then http.status(403, "Forbidden") + http.header("X-LuCI-Login-Required", "yes") return end @@ -597,14 +615,9 @@ function createtree() local ctx = context local tree = {nodes={}, inreq=true} - local modi = {} ctx.treecache = setmetatable({}, {__mode="v"}) ctx.tree = tree - ctx.modifiers = modi - - -- Load default translation - require "luci.i18n".loadc("base") local scope = setmetatable({}, {__index = luci.dispatcher}) @@ -614,28 +627,9 @@ function createtree() v() end - local function modisort(a,b) - return modi[a].order < modi[b].order - end - - for _, v in util.spairs(modi, modisort) do - scope._NAME = v.module - setfenv(v.func, scope) - v.func() - end - return tree end -function modifier(func, order) - context.modifiers[#context.modifiers+1] = { - func = func, - order = order or 0, - module - = getfenv(2)._NAME - } -end - function assign(path, clone, title, order) local obj = node(unpack(path)) obj.nodes = nil @@ -725,24 +719,7 @@ end -- Subdispatchers -- function _find_eligible_node(root, prefix, deep, types, descend) - local _, cur_name, cur_node - local childs = { } - - for cur_name, cur_node in pairs(root.nodes) do - childs[#childs+1] = { - node = cur_node, - name = cur_name, - order = cur_node.order or 100 - } - end - - table.sort(childs, function(a, b) - if a.order == b.order then - return a.name < b.name - else - return a.order < b.order - end - end) + local children = _ordered_children(root) if not root.leaf and deep ~= nil then local sub_path = { unpack(prefix) } @@ -751,10 +728,11 @@ function _find_eligible_node(root, prefix, deep, types, descend) deep = nil end - for _, cur_node in ipairs(childs) do - sub_path[#prefix+1] = cur_node.name + local _, child + for _, child in ipairs(children) do + sub_path[#prefix+1] = child.name - local res_path = _find_eligible_node(cur_node.node, sub_path, + local res_path = _find_eligible_node(child.node, sub_path, deep, types, true) if res_path then diff --git a/modules/luci-base/luasrc/dispatcher.luadoc b/modules/luci-base/luasrc/dispatcher.luadoc index ddf534b3e1..a77f8d8b07 100644 --- a/modules/luci-base/luasrc/dispatcher.luadoc +++ b/modules/luci-base/luasrc/dispatcher.luadoc @@ -22,7 +22,7 @@ Check whether a dispatch node shall be visible ]] ---[[ -Return a sorted table of visible childs within a given node +Return a sorted table of visible children within a given node @class function @name node_childs @@ -82,15 +82,6 @@ Build the index before if it does not exist yet. ]] ---[[ -Register a tree modifier. - -@class function -@name modifier -@param func Modifier function -@param order Modifier order value (optional) -]] - ----[[ Clone a node of the dispatching tree to another position. @class function diff --git a/modules/luci-base/luasrc/http.lua b/modules/luci-base/luasrc/http.lua index f4ede4b8a5..20b55f2854 100644 --- a/modules/luci-base/luasrc/http.lua +++ b/modules/luci-base/luasrc/http.lua @@ -335,13 +335,13 @@ end -- Content-Type. Stores all extracted data associated with its parameter name -- in the params table within the given message object. Multiple parameter -- values are stored as tables, ordinary ones as strings. --- If an optional file callback function is given then it is feeded with the +-- If an optional file callback function is given then it is fed with the -- file contents chunk by chunk and only the extracted file name is stored -- within the params table. The callback function will be called subsequently -- with three arguments: -- o Table containing decoded (name, file) and raw (headers) mime header data -- o String value containing a chunk of the file data --- o Boolean which indicates wheather the current chunk is the last one (eof) +-- o Boolean which indicates whether the current chunk is the last one (eof) function mimedecode_message_body(src, msg, file_cb) local parser, header, field local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil) diff --git a/modules/luci-base/luasrc/http.luadoc b/modules/luci-base/luasrc/http.luadoc index f8121230b6..8f6f380d8b 100644 --- a/modules/luci-base/luasrc/http.luadoc +++ b/modules/luci-base/luasrc/http.luadoc @@ -204,13 +204,13 @@ Stores all extracted data associated with its parameter name in the params table within the given message object. Multiple parameter values are stored as tables, ordinary ones as strings. -If an optional file callback function is given then it is feeded with the +If an optional file callback function is given then it is fed with the file contents chunk by chunk and only the extracted file name is stored within the params table. The callback function will be called subsequently with three arguments: o Table containing decoded (name, file) and raw (headers) mime header data o String value containing a chunk of the file data - o Boolean which indicates wheather the current chunk is the last one (eof) + o Boolean which indicates whether the current chunk is the last one (eof) @class function @name mimedecode_message_body diff --git a/modules/luci-base/luasrc/i18n.lua b/modules/luci-base/luasrc/i18n.lua index bcb16d5c04..323912b650 100644 --- a/modules/luci-base/luasrc/i18n.lua +++ b/modules/luci-base/luasrc/i18n.lua @@ -1,37 +1,43 @@ -- Copyright 2008 Steven Barth <steven@midlink.org> -- Licensed to the public under the Apache License 2.0. -module("luci.i18n", package.seeall) -require("luci.util") +local tparser = require "luci.template.parser" +local util = require "luci.util" +local tostring = tostring -local tparser = require "luci.template.parser" +module "luci.i18n" -table = {} -i18ndir = luci.util.libpath() .. "/i18n/" -loaded = {} -context = luci.util.threadlocal() +i18ndir = util.libpath() .. "/i18n/" +context = util.threadlocal() default = "en" -function clear() -end -function load(file, lang, force) -end +function setlanguage(lang) + local code, subcode = lang:match("^([A-Za-z][A-Za-z])[%-_]([A-Za-z][A-Za-z])$") + if not (code and subcode) then + subcode = lang:match("^([A-Za-z][A-Za-z])$") + if not subcode then + return nil + end + end --- Alternatively load the translation of the fallback language. -function loadc(file, force) -end + context.parent = code and code:lower() + context.lang = context.parent and context.parent.."-"..subcode:lower() or subcode:lower() -function setlanguage(lang) - context.lang = lang:gsub("_", "-") - context.parent = (context.lang:match("^([a-z][a-z])_")) - if not tparser.load_catalog(context.lang, i18ndir) then - if context.parent then - tparser.load_catalog(context.parent, i18ndir) + if tparser.load_catalog(context.lang, i18ndir) and + tparser.change_catalog(context.lang) + then + return context.lang + + elseif context.parent then + if tparser.load_catalog(context.parent, i18ndir) and + tparser.change_catalog(context.parent) + then return context.parent end end - return context.lang + + return nil end function translate(key) @@ -42,14 +48,8 @@ function translatef(key, ...) return tostring(translate(key)):format(...) end --- and ensure that the returned value is a Lua string value. --- This is the same as calling <code>tostring(translate(...))</code> -function string(key) - return tostring(translate(key)) -end - --- Ensure that the returned value is a Lua string value. --- This is the same as calling <code>tostring(translatef(...))</code> -function stringf(key, ...) - return tostring(translate(key)):format(...) +function dump() + local rv = {} + tparser.get_translations(function(k, v) rv[k] = v end) + return rv end diff --git a/modules/luci-base/luasrc/i18n.luadoc b/modules/luci-base/luasrc/i18n.luadoc index aa38841e17..b76c298565 100644 --- a/modules/luci-base/luasrc/i18n.luadoc +++ b/modules/luci-base/luasrc/i18n.luadoc @@ -4,40 +4,12 @@ LuCI translation library. module "luci.i18n" ---[[ -Clear the translation table. - - -@class function -@name clear -]] - ----[[ -Load a translation and copy its data into the translation table. - -@class function -@name load -@param file Language file -@param lang Two-letter language code -@param force Force reload even if already loaded (optional) -@return Success status -]] - ----[[ -Load a translation file using the default translation language. - -Alternatively load the translation of the fallback language. -@class function -@name loadc -@param file Language file -@param force Force reload even if already loaded (optional) -]] - ----[[ Set the context default translation language. @class function @name setlanguage -@param lang Two-letter language code +@param lang An IETF/BCP 47 language tag or ISO3166 country code, e.g. "en-US" or "de" +@return The effective loaded language, e.g. "en" for "en-US" - or nil on failure ]] ---[[ @@ -60,25 +32,11 @@ Return the translated value for a specific translation key and use it as sprintf ]] ---[[ -Return the translated value for a specific translation key +Return all currently loaded translation strings as a key-value table. The key is the +hexadecimal representation of the translation key while the value is the translated +text content. -and ensure that the returned value is a Lua string value. -This is the same as calling <code>tostring(translate(...))</code> @class function -@name string -@param key Default translation text -@return Translated string +@name dump +@return Key-value translation string table. ]] - ----[[ -Return the translated value for a specific translation key and use it as sprintf pattern. - -Ensure that the returned value is a Lua string value. -This is the same as calling <code>tostring(translatef(...))</code> -@class function -@name stringf -@param key Default translation text -@param ... Format parameters -@return Translated and formatted string -]] - diff --git a/modules/luci-base/luasrc/model/cbi/admin_network/proto_dhcp.lua b/modules/luci-base/luasrc/model/cbi/admin_network/proto_dhcp.lua index dc702e4a95..6e04465ac9 100644 --- a/modules/luci-base/luasrc/model/cbi/admin_network/proto_dhcp.lua +++ b/modules/luci-base/luasrc/model/cbi/admin_network/proto_dhcp.lua @@ -53,6 +53,7 @@ metric.datatype = "uinteger" clientid = section:taboption("advanced", Value, "clientid", translate("Client ID to send when requesting DHCP")) +clientid.datatype = "hexstring" vendorclass = section:taboption("advanced", Value, "vendorid", diff --git a/modules/luci-base/luasrc/model/cbi/admin_network/proto_static.lua b/modules/luci-base/luasrc/model/cbi/admin_network/proto_static.lua index 3f8b091cf3..246d2c0ed5 100644 --- a/modules/luci-base/luasrc/model/cbi/admin_network/proto_static.lua +++ b/modules/luci-base/luasrc/model/cbi/admin_network/proto_static.lua @@ -4,17 +4,93 @@ local map, section, net = ... local ifc = net:get_interface() -local ipaddr, netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw -local mtu, metric +local netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw +local mtu, metric, usecidr, ipaddr_single, ipaddr_multi -ipaddr = section:taboption("general", Value, "ipaddr", translate("IPv4 address")) -ipaddr.datatype = "ip4addr" +local function is_cidr(s) + return (type(s) == "string" and luci.ip.IPv4(s) and s:find("/")) +end + +usecidr = section:taboption("general", Value, "ipaddr_usecidr") +usecidr.forcewrite = true + +usecidr.cfgvalue = function(self, section) + local cfgvalue = self.map:get(section, "ipaddr") + return (type(cfgvalue) == "table" or is_cidr(cfgvalue)) and "1" or "0" +end + +usecidr.render = function(self, section, scope) + luci.template.Template(nil, [[ + <input type="hidden"<%= attr("id", cbid) .. attr("name", cbid) .. attr("value", value) %> /> + ]]):render({ + cbid = self:cbid(section), + value = self:cfgvalue(section) + }) +end + +usecidr.write = function(self, section) + local cfgvalue = self.map:get(section, "ipaddr") + local formvalue = (self:formvalue(section) == "1") and ipaddr_multi:formvalue(section) or ipaddr_single:formvalue(section) + local equal = (cfgvalue == formvalue) + + if not equal and type(cfgvalue) == "table" and type(formvalue) == "table" and #cfgvalue == #formvalue then + equal = true + + local _, v + for _, v in ipairs(cfgvalue) do + if v ~= formvalue[_] then + equal = false + break + end + end + end + + if not equal then + self.map:set(section, "ipaddr", formvalue or "") + end + + return not equal +end + + +ipaddr_multi = section:taboption("general", DynamicList, "ipaddrs", translate("IPv4 address")) +ipaddr_multi:depends("ipaddr_usecidr", "1") +ipaddr_multi.datatype = "or(cidr4,ipnet4)" +ipaddr_multi.placeholder = translate("Add IPv4 address…") + +ipaddr_multi.alias = "ipaddr" +ipaddr_multi.write = function() end +ipaddr_multi.remove = function() end +ipaddr_multi.cfgvalue = function(self, section) + local addr = self.map:get(section, "ipaddr") + local mask = self.map:get(section, "netmask") + + if is_cidr(addr) then + return { addr } + elseif type(addr) == "string" and + type(mask) == "string" and + #addr > 0 and #mask > 0 + then + return { "%s/%s" %{ addr, mask } } + elseif type(addr) == "table" then + return addr + else + return {} + end +end + +ipaddr_single = section:taboption("general", Value, "ipaddr", translate("IPv4 address")) +ipaddr_single:depends("ipaddr_usecidr", "0") +ipaddr_single.datatype = "ip4addr" +ipaddr_single.template = "cbi/ipaddr" +ipaddr_single.write = function() end +ipaddr_single.remove = function() end -netmask = section:taboption("general", Value, "netmask", - translate("IPv4 netmask")) +netmask = section:taboption("general", Value, "netmask", translate("IPv4 netmask")) +netmask:depends("ipaddr_usecidr", "0") netmask.datatype = "ip4addr" netmask:value("255.255.255.0") netmask:value("255.255.0.0") @@ -48,8 +124,9 @@ if luci.model.network:has_ipv6() then translate("Assign prefix parts using this hexadecimal subprefix ID for this interface.")) for i=33,64 do ip6hint:depends("ip6assign", i) end - ip6addr = section:taboption("general", Value, "ip6addr", translate("IPv6 address")) + ip6addr = section:taboption("general", DynamicList, "ip6addr", translate("IPv6 address")) ip6addr.datatype = "ip6addr" + ip6addr.placeholder = translate("Add IPv6 address…") ip6addr:depends("ip6assign", "") diff --git a/modules/luci-base/luasrc/model/ipkg.lua b/modules/luci-base/luasrc/model/ipkg.lua deleted file mode 100644 index e27ea52895..0000000000 --- a/modules/luci-base/luasrc/model/ipkg.lua +++ /dev/null @@ -1,247 +0,0 @@ --- Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org> --- Copyright 2008 Steven Barth <steven@midlink.org> --- Licensed to the public under the Apache License 2.0. - -local os = require "os" -local io = require "io" -local fs = require "nixio.fs" -local util = require "luci.util" - -local type = type -local pairs = pairs -local error = error -local table = table - -local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase" -local icfg = "/etc/opkg.conf" - -module "luci.model.ipkg" - - --- Internal action function -local function _action(cmd, ...) - local cmdline = { ipkg, cmd } - - local k, v - for k, v in pairs({...}) do - cmdline[#cmdline+1] = util.shellquote(v) - end - - local c = "%s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" % table.concat(cmdline, " ") - local r = os.execute(c) - local e = fs.readfile("/tmp/opkg.stderr") - local o = fs.readfile("/tmp/opkg.stdout") - - fs.unlink("/tmp/opkg.stderr") - fs.unlink("/tmp/opkg.stdout") - - return r, o or "", e or "" -end - --- Internal parser function -local function _parselist(rawdata) - if type(rawdata) ~= "function" then - error("OPKG: Invalid rawdata given") - end - - local data = {} - local c = {} - local l = nil - - for line in rawdata do - if line:sub(1, 1) ~= " " then - local key, val = line:match("(.-): ?(.*)%s*") - - if key and val then - if key == "Package" then - c = {Package = val} - data[val] = c - elseif key == "Status" then - c.Status = {} - for j in val:gmatch("([^ ]+)") do - c.Status[j] = true - end - else - c[key] = val - end - l = key - end - else - -- Multi-line field - c[l] = c[l] .. "\n" .. line - end - end - - return data -end - --- Internal lookup function -local function _lookup(cmd, pkg) - local cmdline = { ipkg, cmd } - if pkg then - cmdline[#cmdline+1] = util.shellquote(pkg) - end - - -- OPKG sometimes kills the whole machine because it sucks - -- Therefore we have to use a sucky approach too and use - -- tmpfiles instead of directly reading the output - local tmpfile = os.tmpname() - os.execute("%s >%s 2>/dev/null" %{ table.concat(cmdline, " "), tmpfile }) - - local data = _parselist(io.lines(tmpfile)) - os.remove(tmpfile) - return data -end - - -function info(pkg) - return _lookup("info", pkg) -end - -function status(pkg) - return _lookup("status", pkg) -end - -function install(...) - return _action("install", ...) -end - -function installed(pkg) - local p = status(pkg)[pkg] - return (p and p.Status and p.Status.installed) -end - -function remove(...) - return _action("remove", ...) -end - -function update() - return _action("update") -end - -function upgrade() - return _action("upgrade") -end - --- List helper -local function _list(action, pat, cb) - local cmdline = { ipkg, action } - if pat then - cmdline[#cmdline+1] = util.shellquote(pat) - end - - local fd = io.popen(table.concat(cmdline, " ")) - if fd then - local name, version, sz, desc - while true do - local line = fd:read("*l") - if not line then break end - - name, version, sz, desc = line:match("^(.-) %- (.-) %- (.-) %- (.+)") - - if not name then - name, version, sz = line:match("^(.-) %- (.-) %- (.+)") - desc = "" - end - - if name and version then - if #version > 26 then - version = version:sub(1,21) .. ".." .. version:sub(-3,-1) - end - - cb(name, version, sz, desc) - end - - name = nil - version = nil - sz = nil - desc = nil - end - - fd:close() - end -end - -function list_all(pat, cb) - _list("list --size", pat, cb) -end - -function list_installed(pat, cb) - _list("list_installed --size", pat, cb) -end - -function find(pat, cb) - _list("find --size", pat, cb) -end - - -function overlay_root() - local od = "/" - local fd = io.open(icfg, "r") - - if fd then - local ln - - repeat - ln = fd:read("*l") - if ln and ln:match("^%s*option%s+overlay_root%s+") then - od = ln:match("^%s*option%s+overlay_root%s+(%S+)") - - local s = fs.stat(od) - if not s or s.type ~= "dir" then - od = "/" - end - - break - end - until not ln - - fd:close() - end - - return od -end - -function compare_versions(ver1, comp, ver2) - if not ver1 or not ver2 - or not comp or not (#comp > 0) then - error("Invalid parameters") - return nil - end - -- correct compare string - if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~=" - elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<=" - elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">=" - elseif comp == "=" or comp == "==" then comp = "==" - elseif comp == "<<" then comp = "<" - elseif comp == ">>" then comp = ">" - else - error("Invalid compare string") - return nil - end - - local av1 = util.split(ver1, "[%.%-]", nil, true) - local av2 = util.split(ver2, "[%.%-]", nil, true) - - local max = table.getn(av1) - if (table.getn(av1) < table.getn(av2)) then - max = table.getn(av2) - end - - for i = 1, max, 1 do - local s1 = av1[i] or "" - local s2 = av2[i] or "" - - -- first "not equal" found return true - if comp == "~=" and (s1 ~= s2) then return true end - -- first "lower" found return true - if (comp == "<" or comp == "<=") and (s1 < s2) then return true end - -- first "greater" found return true - if (comp == ">" or comp == ">=") and (s1 > s2) then return true end - -- not equal then return false - if (s1 ~= s2) then return false end - end - - -- all equal and not compare greater or lower then true - return not (comp == "<" or comp == ">") -end diff --git a/modules/luci-base/luasrc/model/ipkg.luadoc b/modules/luci-base/luasrc/model/ipkg.luadoc deleted file mode 100644 index 4e1548dda6..0000000000 --- a/modules/luci-base/luasrc/model/ipkg.luadoc +++ /dev/null @@ -1,125 +0,0 @@ ----[[ -LuCI OPKG call abstraction library -]] -module "luci.model.ipkg" - ----[[ -Return information about installed and available packages. - -@class function -@name info -@param pkg Limit output to a (set of) packages -@return Table containing package information -]] - ----[[ -Return the package status of one or more packages. - -@class function -@name status -@param pkg Limit output to a (set of) packages -@return Table containing package status information -]] - ----[[ -Install one or more packages. - -@class function -@name install -@param ... List of packages to install -@return Boolean indicating the status of the action -@return OPKG return code, STDOUT and STDERR -]] - ----[[ -Determine whether a given package is installed. - -@class function -@name installed -@param pkg Package -@return Boolean -]] - ----[[ -Remove one or more packages. - -@class function -@name remove -@param ... List of packages to install -@return Boolean indicating the status of the action -@return OPKG return code, STDOUT and STDERR -]] - ----[[ -Update package lists. - -@class function -@name update -@return Boolean indicating the status of the action -@return OPKG return code, STDOUT and STDERR -]] - ----[[ -Upgrades all installed packages. - -@class function -@name upgrade -@return Boolean indicating the status of the action -@return OPKG return code, STDOUT and STDERR -]] - ----[[ -List all packages known to opkg. - -@class function -@name list_all -@param pat Only find packages matching this pattern, nil lists all packages -@param cb Callback function invoked for each package, receives name, version and description as arguments -@return nothing -]] - ----[[ -List installed packages. - -@class function -@name list_installed -@param pat Only find packages matching this pattern, nil lists all packages -@param cb Callback function invoked for each package, receives name, version and description as arguments -@return nothing -]] - ----[[ -Find packages that match the given pattern. - -@class function -@name find -@param pat Find packages whose names or descriptions match this pattern, nil results in zero results -@param cb Callback function invoked for each patckage, receives name, version and description as arguments -@return nothing -]] - ----[[ -Determines the overlay root used by opkg. - -@class function -@name overlay_root -@return String containing the directory path of the overlay root. -]] - ----[[ -lua version of opkg compare-versions - -@class function -@name compare_versions -@param ver1 string version 1 -@param ver2 string version 2 -@param comp string compare versions using - "<=" or "<" lower-equal - ">" or ">=" greater-equal - "=" equal - "<<" lower - ">>" greater - "~=" not equal -@return Boolean indicating the status of the compare -]] - diff --git a/modules/luci-base/luasrc/model/network.lua b/modules/luci-base/luasrc/model/network.lua index 7f7397032f..a36a23f321 100644 --- a/modules/luci-base/luasrc/model/network.lua +++ b/modules/luci-base/luasrc/model/network.lua @@ -622,6 +622,12 @@ function del_network(self, n) _uci:delete("wireless", s['.name'], "network") end end) + + local ok, fw = pcall(require, "luci.model.firewall") + if ok then + fw.init() + fw:del_network(n) + end end return r end @@ -855,6 +861,14 @@ function get_status_by_address(self, addr) end end end + if s and s['ipv6-prefix-assignment'] then + local a + for _, a in ipairs(s['ipv6-prefix-assignment']) do + if a and a['local-address'] and a['local-address'].address == addr then + return net, s + end + end + end end end end @@ -1151,6 +1165,10 @@ function protocol.is_dynamic(self) return (self:_ubus("dynamic") == true) end +function protocol.is_auto(self) + return (self:_get("auto") ~= "0") +end + function protocol.is_alias(self) local ifn, parent = nil, nil diff --git a/modules/luci-base/luasrc/model/uci.lua b/modules/luci-base/luasrc/model/uci.lua index 2119a210bb..a50e28a871 100644 --- a/modules/luci-base/luasrc/model/uci.lua +++ b/modules/luci-base/luasrc/model/uci.lua @@ -15,7 +15,7 @@ local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack -- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.), -- save the changes to the staging area via Cursor.save and finally -- Cursor.commit the data to the actual config files. --- LuCI then needs to Cursor.apply the changes so deamons etc. are +-- LuCI then needs to Cursor.apply the changes so daemons etc. are -- reloaded. module "luci.model.uci" diff --git a/modules/luci-base/luasrc/model/uci.luadoc b/modules/luci-base/luasrc/model/uci.luadoc index d798b00338..0189d49aa1 100644 --- a/modules/luci-base/luasrc/model/uci.luadoc +++ b/modules/luci-base/luasrc/model/uci.luadoc @@ -5,7 +5,7 @@ The typical workflow for UCI is: Get a cursor instance from the cursor factory, modify data (via Cursor.add, Cursor.delete, etc.), save the changes to the staging area via Cursor.save and finally Cursor.commit the data to the actual config files. -LuCI then needs to Cursor.apply the changes so deamons etc. are +LuCI then needs to Cursor.apply the changes so daemons etc. are reloaded. @cstyle instance ]] @@ -172,7 +172,7 @@ has the same effect as deleting the option. ---[[ Create a sub-state of this cursor. -The sub-state is tied to the parent curser, means it the parent unloads or +The sub-state is tied to the parent cursor, means it the parent unloads or loads configs, the sub state will do so as well. @class function @@ -339,7 +339,7 @@ Set the configuration directory. ]] ---[[ -Set the directory for uncommited changes. +Set the directory for uncommitted changes. @class function @name Cursor.set_savedir diff --git a/modules/luci-base/luasrc/sys.lua b/modules/luci-base/luasrc/sys.lua index 1436a3a235..7e4a9d63cf 100644 --- a/modules/luci-base/luasrc/sys.lua +++ b/modules/luci-base/luasrc/sys.lua @@ -13,8 +13,8 @@ local luci = {} luci.util = require "luci.util" luci.ip = require "luci.ip" -local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select = - tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select +local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select, unpack = + tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select, unpack module "luci.sys" @@ -436,6 +436,96 @@ end process.signal = nixio.kill +local function xclose(fd) + if fd and fd:fileno() > 2 then + fd:close() + end +end + +function process.exec(command, stdout, stderr, nowait) + local out_r, out_w, err_r, err_w + if stdout then out_r, out_w = nixio.pipe() end + if stderr then err_r, err_w = nixio.pipe() end + + local pid = nixio.fork() + if pid == 0 then + nixio.chdir("/") + + local null = nixio.open("/dev/null", "w+") + if null then + nixio.dup(out_w or null, nixio.stdout) + nixio.dup(err_w or null, nixio.stderr) + nixio.dup(null, nixio.stdin) + xclose(out_w) + xclose(out_r) + xclose(err_w) + xclose(err_r) + xclose(null) + end + + nixio.exec(unpack(command)) + os.exit(-1) + end + + local _, pfds, rv = nil, {}, { code = -1, pid = pid } + + xclose(out_w) + xclose(err_w) + + if out_r then + pfds[#pfds+1] = { + fd = out_r, + cb = type(stdout) == "function" and stdout, + name = "stdout", + events = nixio.poll_flags("in", "err", "hup") + } + end + + if err_r then + pfds[#pfds+1] = { + fd = err_r, + cb = type(stderr) == "function" and stderr, + name = "stderr", + events = nixio.poll_flags("in", "err", "hup") + } + end + + while #pfds > 0 do + local nfds, err = nixio.poll(pfds, -1) + if not nfds and err ~= nixio.const.EINTR then + break + end + + local i + for i = #pfds, 1, -1 do + local rfd = pfds[i] + if rfd.revents > 0 then + local chunk, err = rfd.fd:read(4096) + if chunk and #chunk > 0 then + if rfd.cb then + rfd.cb(chunk) + else + rfd.buf = rfd.buf or {} + rfd.buf[#rfd.buf + 1] = chunk + end + else + table.remove(pfds, i) + if rfd.buf then + rv[rfd.name] = table.concat(rfd.buf, "") + end + rfd.fd:close() + end + end + end + end + + if not nowait then + _, _, rv.code = nixio.waitpid(pid) + end + + return rv +end + user = {} diff --git a/modules/luci-base/luasrc/sys.luadoc b/modules/luci-base/luasrc/sys.luadoc index 1c1fa92602..162650e7ac 100644 --- a/modules/luci-base/luasrc/sys.luadoc +++ b/modules/luci-base/luasrc/sys.luadoc @@ -18,7 +18,7 @@ Execute a given shell command and capture its standard output @class function @name exec @param command Command to call -@return String containg the return the output of the command +@return String containing the return the output of the command ]] ---[[ @@ -38,7 +38,7 @@ exists. @class function @name getenv @param var Name of the environment variable to retrieve (optional) -@return String containg the value of the specified variable +@return String containing the value of the specified variable @return Table containing all variables if no variable name is given ]] @@ -272,6 +272,42 @@ Send a signal to a process identified by given pid. ]] ---[[ +Execute a process, optionally capturing stdio. + +Executes the process specified by the given argv vector, e.g. +`{ "/bin/sh", "-c", "echo 1" }` and waits for it to terminate unless a true +value has been passed for the "nowait" parameter. + +When a function value is passed for the stdout or stderr arguments, the passed +function is repeatedly called for each chunk read from the corresponding stdio +stream. The read data is passed as string containing at most 4096 bytes at a +time. + +When a true, non-function value is passed for the stdout or stderr arguments, +the data of the corresponding stdio stream is read into an internal string +buffer and returned as "stdout" or "stderr" field respectively in the result +table. + +When a true value is passed to the nowait parameter, the function does not +await process termination but returns as soon as all captured stdio streams +have been closed or - if no streams are captured - immediately after launching +the process. + +@class function +@name process.exec +@param commend Table containing the argv vector to execute +@param stdout Callback function or boolean to indicate capturing (optional) +@param stderr Callback function or boolean to indicate capturing (optional) +@param nowait Don't wait for process termination when true (optional) +@return Table containing at least the fields "code" which holds the exit + status of the invoked process or "-1" on error and "pid", which + contains the process id assigned to the spawned process. When + stdout and/or stderr capturing has been requested, it additionally + contains "stdout" and "stderr" fields respectively, holding the + captured stdio data as string. +]] + +---[[ LuCI system utilities / user related functions. @class module @@ -279,7 +315,7 @@ LuCI system utilities / user related functions. ]] ---[[ -Retrieve user informations for given uid. +Retrieve user information for given uid. @class function @name getuser @@ -305,7 +341,7 @@ Test whether given string matches the password of a given system user. @name user.checkpasswd @param username String containing the Unix user name @param pass String containing the password to compare -@return Boolean indicating wheather the passwords are equal +@return Boolean indicating whether the passwords are equal ]] ---[[ diff --git a/modules/luci-base/luasrc/sys/iptparser.lua b/modules/luci-base/luasrc/sys/iptparser.lua deleted file mode 100644 index 7ff665e7af..0000000000 --- a/modules/luci-base/luasrc/sys/iptparser.lua +++ /dev/null @@ -1,374 +0,0 @@ ---[[ - -Iptables parser and query library -(c) 2008-2009 Jo-Philipp Wich <jow@openwrt.org> -(c) 2008-2009 Steven Barth <steven@midlink.org> - -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 - -$Id$ - -]]-- - -local luci = {} -luci.util = require "luci.util" -luci.sys = require "luci.sys" -luci.ip = require "luci.ip" - -local pcall = pcall -local io = require "io" -local tonumber, ipairs, table = tonumber, ipairs, table - -module("luci.sys.iptparser") - -IptParser = luci.util.class() - -function IptParser.__init__( self, family ) - self._family = (tonumber(family) == 6) and 6 or 4 - self._rules = { } - self._chains = { } - self._tables = { } - - local t = self._tables - local s = self:_supported_tables(self._family) - - if s.filter then t[#t+1] = "filter" end - if s.nat then t[#t+1] = "nat" end - if s.mangle then t[#t+1] = "mangle" end - if s.raw then t[#t+1] = "raw" end - - if self._family == 4 then - self._nulladdr = "0.0.0.0/0" - self._command = "iptables -t %s --line-numbers -nxvL" - else - self._nulladdr = "::/0" - self._command = "ip6tables -t %s --line-numbers -nxvL" - end - - self:_parse_rules() -end - -function IptParser._supported_tables( self, family ) - local tables = { } - local ok, lines = pcall(io.lines, - (family == 6) and "/proc/net/ip6_tables_names" - or "/proc/net/ip_tables_names") - - if ok and lines then - local line - for line in lines do - tables[line] = true - end - end - - return tables -end - --- search criteria as only argument. If args is nil or an empty table then all --- rules will be returned. --- --- The following keys in the args table are recognized: --- <ul> --- <li> table - Match rules that are located within the given table --- <li> chain - Match rules that are located within the given chain --- <li> target - Match rules with the given target --- <li> protocol - Match rules that match the given protocol, rules with --- protocol "all" are always matched --- <li> source - Match rules with the given source, rules with source --- "0.0.0.0/0" (::/0) are always matched --- <li> destination - Match rules with the given destination, rules with --- destination "0.0.0.0/0" (::/0) are always matched --- <li> inputif - Match rules with the given input interface, rules --- with input interface "*" (=all) are always matched --- <li> outputif - Match rules with the given output interface, rules --- with output interface "*" (=all) are always matched --- <li> flags - Match rules that match the given flags, current --- supported values are "-f" (--fragment) --- and "!f" (! --fragment) --- <li> options - Match rules containing all given options --- </ul> --- The return value is a list of tables representing the matched rules. --- Each rule table contains the following fields: --- <ul> --- <li> index - The index number of the rule --- <li> table - The table where the rule is located, can be one --- of "filter", "nat" or "mangle" --- <li> chain - The chain where the rule is located, e.g. "INPUT" --- or "postrouting_wan" --- <li> target - The rule target, e.g. "REJECT" or "DROP" --- <li> protocol The matching protocols, e.g. "all" or "tcp" --- <li> flags - Special rule options ("--", "-f" or "!f") --- <li> inputif - Input interface of the rule, e.g. "eth0.0" --- or "*" for all interfaces --- <li> outputif - Output interface of the rule,e.g. "eth0.0" --- or "*" for all interfaces --- <li> source - The source ip range, e.g. "0.0.0.0/0" (::/0) --- <li> destination - The destination ip range, e.g. "0.0.0.0/0" (::/0) --- <li> options - A list of specific options of the rule, --- e.g. { "reject-with", "tcp-reset" } --- <li> packets - The number of packets matched by the rule --- <li> bytes - The number of total bytes matched by the rule --- </ul> --- Example: --- <pre> --- ip = luci.sys.iptparser.IptParser() --- result = ip.find( { --- target="REJECT", --- protocol="tcp", --- options={ "reject-with", "tcp-reset" } --- } ) --- </pre> --- This will match all rules with target "-j REJECT", --- protocol "-p tcp" (or "-p all") --- and the option "--reject-with tcp-reset". -function IptParser.find( self, args ) - - local args = args or { } - local rv = { } - - args.source = args.source and self:_parse_addr(args.source) - args.destination = args.destination and self:_parse_addr(args.destination) - - for i, rule in ipairs(self._rules) do - local match = true - - -- match table - if not ( not args.table or args.table:lower() == rule.table ) then - match = false - end - - -- match chain - if not ( match == true and ( - not args.chain or args.chain == rule.chain - ) ) then - match = false - end - - -- match target - if not ( match == true and ( - not args.target or args.target == rule.target - ) ) then - match = false - end - - -- match protocol - if not ( match == true and ( - not args.protocol or rule.protocol == "all" or - args.protocol:lower() == rule.protocol - ) ) then - match = false - end - - -- match source - if not ( match == true and ( - not args.source or rule.source == self._nulladdr or - self:_parse_addr(rule.source):contains(args.source) - ) ) then - match = false - end - - -- match destination - if not ( match == true and ( - not args.destination or rule.destination == self._nulladdr or - self:_parse_addr(rule.destination):contains(args.destination) - ) ) then - match = false - end - - -- match input interface - if not ( match == true and ( - not args.inputif or rule.inputif == "*" or - args.inputif == rule.inputif - ) ) then - match = false - end - - -- match output interface - if not ( match == true and ( - not args.outputif or rule.outputif == "*" or - args.outputif == rule.outputif - ) ) then - match = false - end - - -- match flags (the "opt" column) - if not ( match == true and ( - not args.flags or rule.flags == args.flags - ) ) then - match = false - end - - -- match specific options - if not ( match == true and ( - not args.options or - self:_match_options( rule.options, args.options ) - ) ) then - match = false - end - - -- insert match - if match == true then - rv[#rv+1] = rule - end - end - - return rv -end - - --- through external commands. -function IptParser.resync( self ) - self._rules = { } - self._chain = nil - self:_parse_rules() -end - - -function IptParser.tables( self ) - return self._tables -end - - -function IptParser.chains( self, table ) - local lookup = { } - local chains = { } - for _, r in ipairs(self:find({table=table})) do - if not lookup[r.chain] then - lookup[r.chain] = true - chains[#chains+1] = r.chain - end - end - return chains -end - - --- and "rules". The "rules" field is a table of rule tables. -function IptParser.chain( self, table, chain ) - return self._chains[table:lower()] and self._chains[table:lower()][chain] -end - - -function IptParser.is_custom_target( self, target ) - for _, r in ipairs(self._rules) do - if r.chain == target then - return true - end - end - return false -end - - --- [internal] Parse address according to family. -function IptParser._parse_addr( self, addr ) - if self._family == 4 then - return luci.ip.IPv4(addr) - else - return luci.ip.IPv6(addr) - end -end - --- [internal] Parse iptables output from all tables. -function IptParser._parse_rules( self ) - - for i, tbl in ipairs(self._tables) do - - self._chains[tbl] = { } - - for i, rule in ipairs(luci.util.execl(self._command % tbl)) do - - if rule:find( "^Chain " ) == 1 then - - local crefs - local cname, cpol, cpkt, cbytes = rule:match( - "^Chain ([^%s]*) %(policy (%w+) " .. - "(%d+) packets, (%d+) bytes%)" - ) - - if not cname then - cname, crefs = rule:match( - "^Chain ([^%s]*) %((%d+) references%)" - ) - end - - self._chain = cname - self._chains[tbl][cname] = { - policy = cpol, - packets = tonumber(cpkt or 0), - bytes = tonumber(cbytes or 0), - references = tonumber(crefs or 0), - rules = { } - } - - else - if rule:find("%d") == 1 then - - local rule_parts = luci.util.split( rule, "%s+", nil, true ) - local rule_details = { } - - -- cope with rules that have no target assigned - if rule:match("^%d+%s+%d+%s+%d+%s%s") then - table.insert(rule_parts, 4, nil) - end - - -- ip6tables opt column is usually zero-width - if self._family == 6 then - table.insert(rule_parts, 6, "--") - end - - rule_details["table"] = tbl - rule_details["chain"] = self._chain - rule_details["index"] = tonumber(rule_parts[1]) - rule_details["packets"] = tonumber(rule_parts[2]) - rule_details["bytes"] = tonumber(rule_parts[3]) - rule_details["target"] = rule_parts[4] - rule_details["protocol"] = rule_parts[5] - rule_details["flags"] = rule_parts[6] - rule_details["inputif"] = rule_parts[7] - rule_details["outputif"] = rule_parts[8] - rule_details["source"] = rule_parts[9] - rule_details["destination"] = rule_parts[10] - rule_details["options"] = { } - - for i = 11, #rule_parts do - if #rule_parts[i] > 0 then - rule_details["options"][i-10] = rule_parts[i] - end - end - - self._rules[#self._rules+1] = rule_details - - self._chains[tbl][self._chain].rules[ - #self._chains[tbl][self._chain].rules + 1 - ] = rule_details - end - end - end - end - - self._chain = nil -end - - --- [internal] Return true if optlist1 contains all elements of optlist 2. --- Return false in all other cases. -function IptParser._match_options( self, o1, o2 ) - - -- construct a hashtable of first options list to speed up lookups - local oh = { } - for i, opt in ipairs( o1 ) do oh[opt] = true end - - -- iterate over second options list - -- each string in o2 must be also present in o1 - -- if o2 contains a string which is not found in o1 then return false - for i, opt in ipairs( o2 ) do - if not oh[opt] then - return false - end - end - - return true -end diff --git a/modules/luci-base/luasrc/sys/iptparser.luadoc b/modules/luci-base/luasrc/sys/iptparser.luadoc deleted file mode 100644 index 071e7d52e4..0000000000 --- a/modules/luci-base/luasrc/sys/iptparser.luadoc +++ /dev/null @@ -1,69 +0,0 @@ ----[[ -LuCI iptables parser and query library - -@cstyle instance -]] -module "luci.sys.iptparser" - ----[[ -Create a new iptables parser object. - -@class function -@name IptParser -@param family Number specifying the address family. 4 for IPv4, 6 for IPv6 -@return IptParser instance -]] - ----[[ -Find all firewall rules that match the given criteria. Expects a table with - -search criteria as only argument. If args is nil or an empty table then all -rules will be returned. -]] - ----[[ -Rebuild the internal lookup table, for example when rules have changed - -through external commands. -@class function -@name IptParser.resync -@return nothing -]] - ----[[ -Find the names of all tables. - -@class function -@name IptParser.tables -@return Table of table names. -]] - ----[[ -Find the names of all chains within the given table name. - -@class function -@name IptParser.chains -@param table String containing the table name -@return Table of chain names in the order they occur. -]] - ----[[ -Return the given firewall chain within the given table name. - -@class function -@name IptParser.chain -@param table String containing the table name -@param chain String containing the chain name -@return Table containing the fields "policy", "packets", "bytes" --- and "rules". The "rules" field is a table of rule tables. -]] - ----[[ -Test whether the given target points to a custom chain. - -@class function -@name IptParser.is_custom_target -@param target String containing the target action -@return Boolean indicating whether target is a custom chain. -]] - diff --git a/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua b/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua index 47cb901a5b..39fd4a3c89 100644 --- a/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua +++ b/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua @@ -16,14 +16,12 @@ TZ = { { 'Africa/Brazzaville', 'WAT-1' }, { 'Africa/Bujumbura', 'CAT-2' }, { 'Africa/Cairo', 'EET-2' }, - { 'Africa/Casablanca', 'WET0WEST,M3.5.0,M10.5.0/3' }, { 'Africa/Ceuta', 'CET-1CEST,M3.5.0,M10.5.0/3' }, { 'Africa/Conakry', 'GMT0' }, { 'Africa/Dakar', 'GMT0' }, { 'Africa/Dar es Salaam', 'EAT-3' }, { 'Africa/Djibouti', 'EAT-3' }, { 'Africa/Douala', 'WAT-1' }, - { 'Africa/El Aaiun', 'WET0WEST,M3.5.0,M10.5.0/3' }, { 'Africa/Freetown', 'GMT0' }, { 'Africa/Gaborone', 'CAT-2' }, { 'Africa/Harare', 'CAT-2' }, @@ -51,7 +49,7 @@ TZ = { { 'Africa/Nouakchott', 'GMT0' }, { 'Africa/Ouagadougou', 'GMT0' }, { 'Africa/Porto-Novo', 'WAT-1' }, - { 'Africa/Sao Tome', 'WAT-1' }, + { 'Africa/Sao Tome', 'GMT0' }, { 'Africa/Tripoli', 'EET-2' }, { 'Africa/Tunis', 'CET-1' }, { 'Africa/Windhoek', 'CAT-2' }, @@ -179,7 +177,7 @@ TZ = { { 'America/Resolute', 'CST6CDT,M3.2.0,M11.1.0' }, { 'America/Rio Branco', '<-05>5' }, { 'America/Santarem', '<-03>3' }, - { 'America/Santiago', '<-04>4<-03>,M8.2.6/24,M5.2.6/24' }, + { 'America/Santiago', '<-04>4<-03>,M9.1.6/24,M4.1.6/24' }, { 'America/Santo Domingo', 'AST4' }, { 'America/Sao Paulo', '<-03>3<-02>,M11.1.0/0,M2.3.0/0' }, { 'America/Scoresbysund', '<-01>1<+00>,M3.5.0/0,M10.5.0/1' }, @@ -261,7 +259,7 @@ TZ = { { 'Asia/Macau', 'CST-8' }, { 'Asia/Magadan', '<+11>-11' }, { 'Asia/Makassar', 'WITA-8' }, - { 'Asia/Manila', '<+08>-8' }, + { 'Asia/Manila', 'PST-8' }, { 'Asia/Muscat', '<+04>-4' }, { 'Asia/Nicosia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' }, { 'Asia/Novokuznetsk', '<+07>-7' }, @@ -270,9 +268,10 @@ TZ = { { 'Asia/Oral', '<+05>-5' }, { 'Asia/Phnom Penh', '<+07>-7' }, { 'Asia/Pontianak', 'WIB-7' }, - { 'Asia/Pyongyang', 'KST-8:30' }, + { 'Asia/Pyongyang', 'KST-9' }, { 'Asia/Qatar', '<+03>-3' }, - { 'Asia/Qyzylorda', '<+06>-6' }, + { 'Asia/Qostanay', '<+06>-6' }, + { 'Asia/Qyzylorda', '<+05>-5' }, { 'Asia/Riyadh', '<+03>-3' }, { 'Asia/Sakhalin', '<+11>-11' }, { 'Asia/Samarkand', '<+05>-5' }, @@ -283,7 +282,7 @@ TZ = { { 'Asia/Taipei', 'CST-8' }, { 'Asia/Tashkent', '<+05>-5' }, { 'Asia/Tbilisi', '<+04>-4' }, - { 'Asia/Tehran', '<+0330>-3:30<+0430>,J80/0,J264/0' }, + { 'Asia/Tehran', '<+0330>-3:30<+0430>,J79/24,J263/24' }, { 'Asia/Thimphu', '<+06>-6' }, { 'Asia/Tokyo', 'JST-9' }, { 'Asia/Tomsk', '<+07>-7' }, @@ -358,7 +357,7 @@ TZ = { { 'Europe/Busingen', 'CET-1CEST,M3.5.0,M10.5.0/3' }, { 'Europe/Chisinau', 'EET-2EEST,M3.5.0,M10.5.0/3' }, { 'Europe/Copenhagen', 'CET-1CEST,M3.5.0,M10.5.0/3' }, - { 'Europe/Dublin', 'GMT0IST,M3.5.0/1,M10.5.0' }, + { 'Europe/Dublin', 'IST-1GMT0,M10.5.0,M3.5.0/1' }, { 'Europe/Gibraltar', 'CET-1CEST,M3.5.0,M10.5.0/3' }, { 'Europe/Guernsey', 'GMT0BST,M3.5.0/1,M10.5.0' }, { 'Europe/Helsinki', 'EET-2EEST,M3.5.0/3,M10.5.0/4' }, @@ -400,7 +399,7 @@ TZ = { { 'Europe/Vatican', 'CET-1CEST,M3.5.0,M10.5.0/3' }, { 'Europe/Vienna', 'CET-1CEST,M3.5.0,M10.5.0/3' }, { 'Europe/Vilnius', 'EET-2EEST,M3.5.0/3,M10.5.0/4' }, - { 'Europe/Volgograd', '<+03>-3' }, + { 'Europe/Volgograd', '<+04>-4' }, { 'Europe/Warsaw', 'CET-1CEST,M3.5.0,M10.5.0/3' }, { 'Europe/Zagreb', 'CET-1CEST,M3.5.0,M10.5.0/3' }, { 'Europe/Zaporozhye', 'EET-2EEST,M3.5.0/3,M10.5.0/4' }, @@ -421,11 +420,11 @@ TZ = { { 'Pacific/Bougainville', '<+11>-11' }, { 'Pacific/Chatham', '<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45' }, { 'Pacific/Chuuk', '<+10>-10' }, - { 'Pacific/Easter', '<-06>6<-05>,M8.2.6/22,M5.2.6/22' }, + { 'Pacific/Easter', '<-06>6<-05>,M9.1.6/22,M4.1.6/22' }, { 'Pacific/Efate', '<+11>-11' }, { 'Pacific/Enderbury', '<+13>-13' }, { 'Pacific/Fakaofo', '<+13>-13' }, - { 'Pacific/Fiji', '<+12>-12<+13>,M11.1.0,M1.2.1/147' }, + { 'Pacific/Fiji', '<+12>-12<+13>,M11.1.0,M1.2.2/123' }, { 'Pacific/Funafuti', '<+12>-12' }, { 'Pacific/Galapagos', '<-06>6' }, { 'Pacific/Gambier', '<-09>9' }, diff --git a/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua b/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua index cf5afeb9d8..e63e2a6958 100644 --- a/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua +++ b/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua @@ -9,7 +9,6 @@ OFFSET = { wat = 3600, -- WAT cat = 7200, -- CAT eet = 7200, -- EET - wet = 0, -- WET sast = 7200, -- SAST hst = -36000, -- HST hdt = -32400, -- HDT @@ -34,8 +33,9 @@ OFFSET = { idt = 10800, -- IDT pkt = 18000, -- PKT wita = 28800, -- WITA - kst = 30600, -- KST + kst = 32400, -- KST jst = 32400, -- JST + wet = 0, -- WET acst = 34200, -- ACST acdt = 37800, -- ACDT aest = 36000, -- AEST diff --git a/modules/luci-base/luasrc/template.lua b/modules/luci-base/luasrc/template.lua index 588028c2ee..ed46f50753 100644 --- a/modules/luci-base/luasrc/template.lua +++ b/modules/luci-base/luasrc/template.lua @@ -95,6 +95,6 @@ function Template.render(self, scope) local stat, err = util.copcall(self.template) if not stat then error("Failed to execute template '" .. self.name .. "'.\n" .. - "A runtime error occured: " .. tostring(err or "(nil)")) + "A runtime error occurred: " .. tostring(err or "(nil)")) end end diff --git a/modules/luci-base/luasrc/util.lua b/modules/luci-base/luasrc/util.lua index f16b3afb2e..1a329f3f20 100644 --- a/modules/luci-base/luasrc/util.lua +++ b/modules/luci-base/luasrc/util.lua @@ -262,7 +262,7 @@ end -- one token per invocation, the tokens are separated by whitespace. If the -- input value is a table, it is transformed into a string first. A nil value --- will result in a valid interator which aborts with the first invocation. +-- will result in a valid iterator which aborts with the first invocation. function imatch(v) if type(v) == "table" then local k = nil diff --git a/modules/luci-base/luasrc/util.luadoc b/modules/luci-base/luasrc/util.luadoc index c4f28d039a..4ec68dd1ef 100644 --- a/modules/luci-base/luasrc/util.luadoc +++ b/modules/luci-base/luasrc/util.luadoc @@ -158,7 +158,7 @@ Return a matching iterator for the given value. The iterator will return one token per invocation, the tokens are separated by whitespace. If the input value is a table, it is transformed into a string first. -A nil value will result in a valid interator which aborts with the first invocation. +A nil value will result in a valid iterator which aborts with the first invocation. @class function @name imatch @@ -289,7 +289,7 @@ will be stripped before it is returned. ]] ---[[ -Strips unnescessary lua bytecode from given string. +Strips unnecessary lua bytecode from given string. Information like line numbers and debugging numbers will be discarded. Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html) diff --git a/modules/luci-base/luasrc/view/admin_uci/changelog.htm b/modules/luci-base/luasrc/view/admin_uci/changelog.htm index 8a162c88b6..9d6267cf31 100644 --- a/modules/luci-base/luasrc/view/admin_uci/changelog.htm +++ b/modules/luci-base/luasrc/view/admin_uci/changelog.htm @@ -19,7 +19,7 @@ local util = luci.util local tpl = { ["add-3"] = "<ins>uci add %0 <strong>%3</strong> # =%2</ins>", - ["set-3"] = "<ins>uci set %0.<strong>%2</strong></ins>=%3", + ["set-3"] = "<ins>uci set %0.<strong>%2</strong>=%3</ins>", ["set-4"] = "<var><ins>uci set %0.%2.%3=<strong>%4</strong></ins></var>", ["remove-2"] = "<del>uci del %0.<strong>%2</strong></del>", ["remove-3"] = "<var><del>uci del %0.%2.<strong>%3</strong></del></var>", diff --git a/modules/luci-base/luasrc/view/cbi/apply_widget.htm b/modules/luci-base/luasrc/view/cbi/apply_widget.htm index ce279edd40..0f9667390e 100644 --- a/modules/luci-base/luasrc/view/cbi/apply_widget.htm +++ b/modules/luci-base/luasrc/view/cbi/apply_widget.htm @@ -1,49 +1,4 @@ <% export("cbi_apply_widget", function(redirect_ok, rollback_token) -%> -<style type="text/css"> - #cbi_apply_overlay { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - background: rgba(0, 0, 0, 0.7); - display: none; - z-index: 20000; - } - - #cbi_apply_overlay .alert-message { - position: relative; - top: 10%; - width: 60%; - margin: auto; - display: flex; - flex-wrap: wrap; - min-height: 32px; - align-items: center; - } - - #cbi_apply_overlay .alert-message > h4, - #cbi_apply_overlay .alert-message > p, - #cbi_apply_overlay .alert-message > div { - flex-basis: 100%; - } - - #cbi_apply_overlay .alert-message > img { - margin-right: 1em; - flex-basis: 32px; - } - - body.apply-overlay-active { - overflow: hidden; - height: 100vh; - } - - body.apply-overlay-active #cbi_apply_overlay { - display: block; - } -</style> - -<script type="text/javascript" src="<%=resource%>/cbi.js?v=git-18.138.59467-72fe5dd"></script> <script type="text/javascript">//<![CDATA[ var xhr = new XHR(), uci_apply_auth = { sid: '<%=luci.dispatcher.context.authsession%>', token: '<%=token%>' }, @@ -55,28 +10,22 @@ was_xhr_poll_running = false; function uci_status_message(type, content) { - var overlay = document.getElementById('cbi_apply_overlay') || document.body.appendChild(E('<div id="cbi_apply_overlay"><div class="alert-message"></div></div>')), - message = overlay.querySelector('.alert-message'); - - if (message && type) { - if (!message.classList.contains(type)) { - message.classList.remove('notice'); - message.classList.remove('warning'); - message.classList.add(type); - } + if (type) { + var message = showModal('', ''); + + message.classList.add('alert-message'); + DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/)); if (content) message.innerHTML = content; - document.body.classList.add('apply-overlay-active'); - if (!was_xhr_poll_running) { was_xhr_poll_running = XHR.running(); XHR.halt(); } } else { - document.body.classList.remove('apply-overlay-active'); + hideModal(); if (was_xhr_poll_running) XHR.run(); @@ -85,19 +34,18 @@ function uci_rollback(checked) { if (checked) { - uci_status_message('warning', - '<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' + - '<%:Failed to confirm apply within %ds, waiting for rollback…%>'.format(uci_apply_rollback)); + uci_status_message('warning spinning', + '<p><%:Failed to confirm apply within %ds, waiting for rollback…%></p>'.format(uci_apply_rollback)); var call = function(r, data, duration) { if (r.status === 204) { uci_status_message('warning', '<h4><%:Configuration has been rolled back!%></h4>' + - '<p><%:The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, perform an unchecked configuration apply. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.%></p>'.format(uci_apply_rollback) + + '<p><%:The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, proceed by applying anyway. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.%></p>'.format(uci_apply_rollback) + '<div class="right">' + '<input type="button" class="btn" onclick="uci_status_message(false)" value="<%:Dismiss%>" /> ' + '<input type="button" class="btn cbi-button-action important" onclick="uci_revert()" value="<%:Revert changes%>" /> ' + - '<input type="button" class="btn cbi-button-negative important" onclick="uci_apply(false)" value="<%:Apply unchecked%>" />' + + '<input type="button" class="btn cbi-button-negative important" onclick="uci_apply(false)" value="<%:Apply anyway%>" />' + '</div>'); return; @@ -126,6 +74,7 @@ var call = function(r, data, duration) { if (Date.now() >= deadline) { + window.clearTimeout(tt); uci_rollback(checked); return; } @@ -133,7 +82,7 @@ var indicator = document.querySelector('.uci_change_indicator'); if (indicator) indicator.style.display = 'none'; - uci_status_message('notice', '<%:Configuration has been applied.%>'); + uci_status_message('notice', '<p><%:Configuration has been applied.%></p>'); window.clearTimeout(tt); window.setTimeout(function() { @@ -156,9 +105,8 @@ var tick = function() { var now = Date.now(); - uci_status_message('notice', - '<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' + - '<%:Waiting for configuration to be applied… %ds%>'.format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0))); + uci_status_message('notice spinning', + '<p><%:Waiting for configuration to be applied… %ds%></p>'.format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0))); if (now >= deadline) return; @@ -174,9 +122,7 @@ } function uci_apply(checked) { - uci_status_message('notice', - '<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' + - '<%:Starting configuration apply…%>'); + uci_status_message('notice spinning', '<p><%:Starting configuration apply…%></p>'); xhr.post('<%=url("admin/uci")%>/' + (checked ? 'apply_rollback' : 'apply_unchecked'), uci_apply_auth, function(r, tok) { if (r.status === (checked ? 200 : 204)) { @@ -186,7 +132,7 @@ uci_confirm(checked, Date.now() + uci_apply_rollback * 1000); } else if (checked && r.status === 204) { - uci_status_message('notice', '<%:There are no changes to apply.%>'); + uci_status_message('notice', '<p><%:There are no changes to apply.%></p>'); window.setTimeout(function() { <% if redirect_ok then -%> location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>'); @@ -196,20 +142,18 @@ }, uci_apply_display * 1000); } else { - uci_status_message('warning', '<%_Apply request failed with status <code>%h</code>%>'.format(r.responseText || r.statusText || r.status)); + uci_status_message('warning', '<p><%_Apply request failed with status <code>%h</code>%></p>'.format(r.responseText || r.statusText || r.status)); window.setTimeout(function() { uci_status_message(false); }, uci_apply_display * 1000); } }); } function uci_revert() { - uci_status_message('notice', - '<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' + - '<%:Reverting configuration…%>'); + uci_status_message('notice spinning', '<p><%:Reverting configuration…%></p>'); xhr.post('<%=url("admin/uci/revert")%>', uci_apply_auth, function(r) { if (r.status === 200) { - uci_status_message('notice', '<%:Changes have been reverted.%>'); + uci_status_message('notice', '<p><%:Changes have been reverted.%></p>'); window.setTimeout(function() { <% if redirect_ok then -%> location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>'); @@ -219,7 +163,7 @@ }, uci_apply_display * 1000); } else { - uci_status_message('warning', '<%_Revert request failed with status <code>%h</code>%>'.format(r.statusText || r.status)); + uci_status_message('warning', '<p><%_Revert request failed with status <code>%h</code>%></p>'.format(r.statusText || r.status)); window.setTimeout(function() { uci_status_message(false); }, uci_apply_display * 1000); } }); diff --git a/modules/luci-base/luasrc/view/cbi/browser.htm b/modules/luci-base/luasrc/view/cbi/browser.htm index 2abc975e8d..eb47ffafe6 100644 --- a/modules/luci-base/luasrc/view/cbi/browser.htm +++ b/modules/luci-base/luasrc/view/cbi/browser.htm @@ -1,8 +1,10 @@ -<% local v = self:cfgvalue(section) -%> <%+cbi/valueheader%> - <input class="cbi-input-text" type="text"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> /> - <script type="text/javascript"> -cbi_init() -cbi_browser_init('<%=cbid%>', '<%=resource%>', '<%=url('admin/filebrowser')%>'<%=self.default_path and ", '"..self.default_path.."'"%>); - </script> + +<input class="cbi-input-text" type="text"<%= + attr("id", cbid) .. + attr("name", cbid) .. + attr("value", self:cfgvalue(section) or self.default) .. + attr("data-browser", self.default_path or "") +%> /> + <%+cbi/valuefooter%> diff --git a/modules/luci-base/luasrc/view/cbi/cell_valueheader.htm b/modules/luci-base/luasrc/view/cbi/cell_valueheader.htm index ea0568f409..cb11d8f61a 100644 --- a/modules/luci-base/luasrc/view/cbi/cell_valueheader.htm +++ b/modules/luci-base/luasrc/view/cbi/cell_valueheader.htm @@ -6,7 +6,7 @@ <div class="td cbi-value-field<% if self.error and self.error[section] then %> cbi-value-error<% end %>"<%= attr("data-name", self.option) .. ifattr(ftype and #ftype > 0, "data-type", ftype) .. - ifattr(title and #title > 0, "data-title", title) .. - ifattr(descr and #descr > 0, "data-description", descr) + ifattr(title and #title > 0, "data-title", title, true) .. + ifattr(descr and #descr > 0, "data-description", descr, true) %>> <div id="cbi-<%=self.config.."-"..section.."-"..self.option%>" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>"> diff --git a/modules/luci-base/luasrc/view/cbi/dropdown.htm b/modules/luci-base/luasrc/view/cbi/dropdown.htm index cf8c03d22c..6f4b89905b 100644 --- a/modules/luci-base/luasrc/view/cbi/dropdown.htm +++ b/modules/luci-base/luasrc/view/cbi/dropdown.htm @@ -30,7 +30,7 @@ <li<%= attr("data-index", i) .. attr("data-depends", self:deplist2json(section, self.deplist[i])) .. - attr("value", key) .. + attr("data-value", key) .. ifattr(selected[key], "selected", "selected") %>> <%=pcdata(self.vallist[i])%> diff --git a/modules/luci-base/luasrc/view/cbi/dynlist.htm b/modules/luci-base/luasrc/view/cbi/dynlist.htm index 4d0b50942b..fa7dbdb418 100644 --- a/modules/luci-base/luasrc/view/cbi/dynlist.htm +++ b/modules/luci-base/luasrc/view/cbi/dynlist.htm @@ -6,22 +6,8 @@ self.keylist, self.vallist, self.datatype, self.optional or self.rmempty })) .. - + attr("data-values", luci.util.serialize_json(self:cfgvalue(section))) .. ifattr(self.size, "data-size", self.size) .. ifattr(self.placeholder, "data-placeholder", self.placeholder) -%>> -<% - local vals = self:cfgvalue(section) or {} - for i=1, #vals + 1 do - local val = vals[i] - if (val and #val > 0) or (i == 1) then -%> - <input class="cbi-input-text" value="<%=pcdata(val)%>" data-update="change" type="text"<%= - attr("id", cbid .. "." .. i) .. - attr("name", cbid) .. - ifattr(self.size, "size") .. - ifattr(i == 1 and self.placeholder, "placeholder", self.placeholder) - %> /><br /> -<% end end %> -</div> +%>></div> <%+cbi/valuefooter%> diff --git a/modules/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm b/modules/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm index b38e4b13db..dc251dbd94 100644 --- a/modules/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm +++ b/modules/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm @@ -63,7 +63,7 @@ if empty then %> <label class="zonebadge zonebadge-empty"> - <strong><%=zone:forward():upper()%></strong> + <strong><%=def:forward():upper()%></strong> </label> <% end %> </div> diff --git a/modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm b/modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm index 3a108020b6..7ecec10a8f 100644 --- a/modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm +++ b/modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm @@ -30,7 +30,7 @@ ifattr(self.rmempty or self.optional, "optional", "optional") %>> <script type="item-template"><!-- - <li value="{{value}}"> + <li data-value="{{value}}"> <span class="zonebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)"> <strong>{{value}}:</strong><em>(<%:create%>)</em> </span> @@ -38,7 +38,7 @@ --></script> <ul> <% if self.allowlocal then %> - <li value=""<%=ifattr(checked[""], "selected", "selected")%>> + <li data-value=""<%=ifattr(checked[""], "selected", "selected")%>> <span style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge"> <strong><%:Device%></strong> <% if self.allowany and self.allowlocal then -%> @@ -48,14 +48,14 @@ </span> </li> <% elseif self.widget ~= "checkbox" and (self.rmempty or self.optional) then %> - <li value=""<%=ifattr(checked[""], "selected", "selected")%>> + <li data-value=""<%=ifattr(checked[""], "selected", "selected")%>> <span class="zonebadge"> <em><%:unspecified%></em> </span> </li> <% end %> <% if self.allowany then %> - <li value="*"<%=ifattr(checked["*"], "selected", "selected")%>> + <li data-value="*"<%=ifattr(checked["*"], "selected", "selected")%>> <span style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge"> <strong><%:Any zone%></strong> <% if self.allowany and self.allowlocal then %>(<%:forward%>)<% end %> @@ -67,7 +67,7 @@ if zone:name() ~= self.exclude then selected = selected or (value == zone:name()) %> - <li<%=attr("value", zone:name()) .. ifattr(checked[zone:name()], "selected", "selected")%>> + <li<%=attr("data-value", zone:name()) .. ifattr(checked[zone:name()], "selected", "selected")%>> <span style="background-color:<%=zone:get_color()%>" class="zonebadge"> <strong><%=zone:name()%>:</strong> <%- @@ -94,11 +94,11 @@ <% end end %> <% if self.widget ~= "checkbox" and not self.nocreate then %> - <li value="-"> + <li data-value="-"> <span class="zonebadge"> <em><%:create%>:</em> <input type="password" style="display:none" /> - <input class="create-item-input" type="text" /> + <input class="create-item-input" type="text" data-type="and(uciname,maxlength(11))" data-optional="true" /> </span> </li> <% end %> diff --git a/modules/luci-base/luasrc/view/cbi/ipaddr.htm b/modules/luci-base/luasrc/view/cbi/ipaddr.htm new file mode 100644 index 0000000000..1c924e1544 --- /dev/null +++ b/modules/luci-base/luasrc/view/cbi/ipaddr.htm @@ -0,0 +1,27 @@ +<%+cbi/valueheader%> + <script type="text/javascript"> + function switchToCIDRList(ev) { + var input = ev.target.previousElementSibling, + usecidr = document.getElementById(input.id + '_usecidr'); + + ev.preventDefault(); + + usecidr.value = '1'; + cbi_d_update(); + } + </script> + <input data-update="change"<%= + attr("id", cbid) .. + attr("name", cbid) .. + attr("type", "text") .. + attr("class", "cbi-input-text") .. + attr("value", self:cfgvalue(section) or self.default) .. + ifattr(self.size, "size") .. + ifattr(self.placeholder, "placeholder") .. + ifattr(self.datatype, "data-type", self.datatype) .. + ifattr(self.datatype, "data-optional", self.optional or self.rmempty) .. + ifattr(self.combobox_manual, "data-manual", self.combobox_manual) .. + ifattr(#self.keylist > 0, "data-choices", { self.keylist, self.vallist }) + %> /><!-- + --><button class="cbi-button cbi-button-neutral" title="<%:Switch to CIDR list notation%>" aria-label="<%:Switch to CIDR list notation%>" onclick="switchToCIDRList(event)">…</button> +<%+cbi/valuefooter%> diff --git a/modules/luci-base/luasrc/view/cbi/map.htm b/modules/luci-base/luasrc/view/cbi/map.htm index d65a161673..cda4d3530c 100644 --- a/modules/luci-base/luasrc/view/cbi/map.htm +++ b/modules/luci-base/luasrc/view/cbi/map.htm @@ -3,25 +3,25 @@ <%- end end -%> <div class="cbi-map" id="cbi-<%=self.config%>"> - <% if self.title and #self.title > 0 then %><h2 name="content"><%=self.title%></h2><% end %> - <% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %> + <% if self.title and #self.title > 0 then %> + <h2 name="content"><%=self.title%></h2> + <% end %> + <% if self.description and #self.description > 0 then %> + <div class="cbi-map-descr"><%=self.description%></div> + <% end %> <% if self.tabbed then %> - <ul class="cbi-tabmenu map"> - <%- self.selected_tab = luci.http.formvalue("tab.m-" .. self.config) %> - <% for i, section in ipairs(self.children) do %> - <%- if not self.selected_tab then self.selected_tab = section.sectiontype end %> - <li id="tab.m-<%=self.config%>.<%=section.section or section.sectiontype%>" class="cbi-tab<%=(section.sectiontype == self.selected_tab) and '' or '-disabled'%>"> - <a onclick="this.blur(); return cbi_t_switch('m-<%=self.config%>', '<%=section.section or section.sectiontype%>')" href="<%=REQUEST_URI%>?tab.m-<%=self.config%>=<%=section.section or section.sectiontype%>"><%=section.title or section.section or section.sectiontype %></a> - <% if section.sectiontype == self.selected_tab then %><input type="hidden" id="tab.m-<%=self.config%>" name="tab.m-<%=self.config%>" value="<%=section.section or section.sectiontype%>" /><% end %> - </li> + <div> + <% for i, section in ipairs(self.children) do + tab = section.section or section.sectiontype %> + <div class="cbi-tabcontainer"<%= + attr("id", "container.m-%s.%s" %{ self.config, tab }) .. + attr("data-tab", tab) .. + attr("data-tab-title", section.title or tab) + %>> + <% section:render() %> + </div> <% end %> - </ul> - <% for i, section in ipairs(self.children) do %> - <div class="cbi-tabcontainer" id="container.m-<%=self.config%>.<%=section.section or section.sectiontype%>"<% if section.sectiontype ~= self.selected_tab then %> style="display:none"<% end %>> - <% section:render() %> - </div> - <script type="text/javascript">cbi_t_add('m-<%=self.config%>', '<%=section.section or section.sectiontype%>')</script> - <% end %> + </div> <% if not self.save then -%> <div class="cbi-section-error"> diff --git a/modules/luci-base/luasrc/view/cbi/network_ifacelist.htm b/modules/luci-base/luasrc/view/cbi/network_ifacelist.htm index a97e9ef6d0..f23e51d18d 100644 --- a/modules/luci-base/luasrc/view/cbi/network_ifacelist.htm +++ b/modules/luci-base/luasrc/view/cbi/network_ifacelist.htm @@ -41,13 +41,13 @@ <input type="hidden" name="<%=cbeid%>" value="1" /> -<div class="cbi-dropdown" display-items="5" placeholder="<%:-- please select -- %>"<%= +<div class="cbi-dropdown" display-items="10" placeholder="<%:-- please select -- %>"<%= attr("name", cbid) .. ifattr(self.widget == "checkbox", "multiple", "multiple") .. ifattr(self.widget == "checkbox", "optional", "optional") %>> <script type="item-template"><!-- - <li value="{{value}}"> + <li data-value="{{value}}"> <img title="<%:Custom Interface%>: "{{value}}"" src="<%=resource%>/icons/ethernet_disabled.png" /> <span class="hide-open">{{value}}</span> <span class="hide-close"><%:Custom Interface%>: "{{value}}"</span> @@ -61,7 +61,7 @@ iface:name() ~= self.exclude then %> <li<%= - attr("value", iface:name()) .. + attr("data-value", iface:name()) .. ifattr(checked[iface:name()], "selected", "selected") %>> <img<%=attr("title", iface:get_i18n())%> src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" /> @@ -78,7 +78,7 @@ </li> <% end end %> <% if not self.nocreate then %> - <li value=""> + <li data-value=""> <img title="<%:Custom Interface%>" src="<%=resource%>/icons/ethernet_disabled.png" /> <span><%:Custom Interface%>:</span> <input type="password" style="display:none" /> diff --git a/modules/luci-base/luasrc/view/cbi/network_netlist.htm b/modules/luci-base/luasrc/view/cbi/network_netlist.htm index ba6ebb8434..3ee4274a33 100644 --- a/modules/luci-base/luasrc/view/cbi/network_netlist.htm +++ b/modules/luci-base/luasrc/view/cbi/network_netlist.htm @@ -20,13 +20,13 @@ end -%> -<div class="cbi-dropdown" display-items="5" placeholder="<%:-- please select -- %>"<%= +<div class="cbi-dropdown" display-items="10" placeholder="<%:-- please select -- %>"<%= attr("name", cbid) .. ifattr(self.widget == "checkbox", "multiple", "multiple") .. ifattr(self.widget == "checkbox", "optional", "optional") %>> <script type="item-template"><!-- - <li value="{{value}}"> + <li data-value="{{value}}"> <span class="ifacebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)"> {{value}}: <em>(<%:create%>)</em> </span> @@ -34,7 +34,7 @@ --></script> <ul> <% if self.widget ~= "checkbox" then %> - <li value=""<%= ifattr(not value, "selected", "selected") %>> + <li data-value=""<%= ifattr(not value, "selected", "selected") %>> <em><%:unspecified%></em> </li> <% end %> @@ -44,7 +44,7 @@ (net:name() ~= self.exclude) and (not self.novirtual or not net:is_virtual()) then %> - <li<%= attr("value", net:name()) .. ifattr(checked[net:name()], "selected", "selected") %>> + <li<%= attr("data-value", net:name()) .. ifattr(checked[net:name()], "selected", "selected") %>> <span class="ifacebadge"><%=net:name()%>: <% local empty = true @@ -63,7 +63,7 @@ <% end end %> <% if not self.nocreate then %> - <li value="-"<%= ifattr(not value and self.widget ~= "checkbox", "selected", "selected") %>> + <li data-value="-"<%= ifattr(not value and self.widget ~= "checkbox", "selected", "selected") %>> <em> <%- if self.widget == "checkbox" then -%> <%:create:%> diff --git a/modules/luci-base/luasrc/view/cbi/nsection.htm b/modules/luci-base/luasrc/view/cbi/nsection.htm index 63abc57734..14232e3d94 100644 --- a/modules/luci-base/luasrc/view/cbi/nsection.htm +++ b/modules/luci-base/luasrc/view/cbi/nsection.htm @@ -11,7 +11,6 @@ <input type="submit" class="cbi-button" name="cbi.rns.<%=self.config%>.<%=section%>" value="<%:Delete%>" /> </div> <%- end %> - <%+cbi/tabmenu%> <div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>"> <%+cbi/ucisection%> </div> diff --git a/modules/luci-base/luasrc/view/cbi/tabcontainer.htm b/modules/luci-base/luasrc/view/cbi/tabcontainer.htm index 38c435d6a1..7fcb835783 100644 --- a/modules/luci-base/luasrc/view/cbi/tabcontainer.htm +++ b/modules/luci-base/luasrc/view/cbi/tabcontainer.htm @@ -1,7 +1,14 @@ -<% for tab, data in pairs(self.tabs) do %> - <div class="cbi-tabcontainer" id="container.<%=self.config%>.<%=section%>.<%=tab%>"<% if tab ~= self.selected_tab then %> style="display:none"<% end %>> - <% if data.description then %><div class="cbi-tab-descr"><%=data.description%></div><% end %> +<% for _, tab in ipairs(self.tab_names) do data = self.tabs[tab] %> + <div class="cbi-tabcontainer"<%= + attr("id", "container.%s.%s.%s" %{ self.config, section, tab }) .. + attr("data-tab", tab) .. + attr("data-tab-title", data.title) .. + attr("data-tab-active", tostring(tab == self.selected_tab)) + %>> + <% if data.description then %> + <div class="cbi-tab-descr"><%=data.description%></div> + <% end %> + <% self:render_tab(tab, section, scope or {}) %> </div> - <script type="text/javascript">cbi_t_add('<%=self.config%>.<%=section%>', '<%=tab%>')</script> <% end %> diff --git a/modules/luci-base/luasrc/view/cbi/tabmenu.htm b/modules/luci-base/luasrc/view/cbi/tabmenu.htm deleted file mode 100644 index 06c1414bf3..0000000000 --- a/modules/luci-base/luasrc/view/cbi/tabmenu.htm +++ /dev/null @@ -1,12 +0,0 @@ -<%- if self.tabs then %> - <ul class="cbi-tabmenu"> - <%- self.selected_tab = luci.http.formvalue("tab." .. self.config .. "." .. section) %> - <%- for _, tab in ipairs(self.tab_names) do if #self.tabs[tab].childs > 0 then %> - <%- if not self.selected_tab then self.selected_tab = tab end %> - <li id="tab.<%=self.config%>.<%=section%>.<%=tab%>" class="cbi-tab<%=(tab == self.selected_tab) and '' or '-disabled'%>"> - <a onclick="this.blur(); return cbi_t_switch('<%=self.config%>.<%=section%>', '<%=tab%>')" href="<%=REQUEST_URI%>?tab.<%=self.config%>.<%=section%>=<%=tab%>"><%=self.tabs[tab].title%></a> - <% if tab == self.selected_tab then %><input type="hidden" id="tab.<%=self.config%>.<%=section%>" name="tab.<%=self.config%>.<%=section%>" value="<%=tab%>" /><% end %> - </li> - <% end end -%> - </ul> -<% end -%> diff --git a/modules/luci-base/luasrc/view/cbi/tblsection.htm b/modules/luci-base/luasrc/view/cbi/tblsection.htm index 408dfa7fe8..11c2206d8c 100644 --- a/modules/luci-base/luasrc/view/cbi/tblsection.htm +++ b/modules/luci-base/luasrc/view/cbi/tblsection.htm @@ -127,7 +127,7 @@ end section = k local sectionname = striptags((type(self.sectiontitle) == "function") and self:sectiontitle(section) or k) - local sectiontitle = ifattr(sectionname and (not self.anonymous or self.sectiontitle), "data-title", sectionname) + local sectiontitle = ifattr(sectionname and (not self.anonymous or self.sectiontitle), "data-title", sectionname, true) local colorclass = (self.extedit or self.rowcolors) and rowstyle() or "" local scope = { valueheader = "cbi/cell_valueheader", diff --git a/modules/luci-base/luasrc/view/cbi/tsection.htm b/modules/luci-base/luasrc/view/cbi/tsection.htm index 1a13df0c04..8f3b7f0ffb 100644 --- a/modules/luci-base/luasrc/view/cbi/tsection.htm +++ b/modules/luci-base/luasrc/view/cbi/tsection.htm @@ -2,6 +2,11 @@ <% if self.title and #self.title > 0 then -%> <legend><%=self.title%></legend> <%- end %> + <% if self.error_msg and #self.error_msg > 0 then -%> + <div class="cbi-section-error"> + <%=self.error_msg%> + </div> + <%- end %> <% if self.description and #self.description > 0 then -%> <div class="cbi-section-descr"><%=self.description%></div> <%- end %> @@ -18,8 +23,6 @@ <h3><%=section:upper()%></h3> <%- end %> - <%+cbi/tabmenu%> - <div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>"> <%+cbi/ucisection%> </div> diff --git a/modules/luci-base/luasrc/view/cbi/value.htm b/modules/luci-base/luasrc/view/cbi/value.htm index 8eec865348..27f3cb2bd9 100644 --- a/modules/luci-base/luasrc/view/cbi/value.htm +++ b/modules/luci-base/luasrc/view/cbi/value.htm @@ -21,6 +21,6 @@ ifattr(#self.keylist > 0, "data-choices", { self.keylist, self.vallist }) %> /> <%- if self.password then -%> - <div class="cbi-button cbi-button-neutral" title="<%:Reveal/hide password%>" onclick="var e = this.previousElementSibling; e.type = (e.type === 'password') ? 'text' : 'password'">∗</div> + <button class="cbi-button cbi-button-neutral" title="<%:Reveal/hide password%>" aria-label="<%:Reveal/hide password%>" onclick="var e = this.previousElementSibling; e.type = (e.type === 'password') ? 'text' : 'password'; event.preventDefault()">∗</button> <% end %> <%+cbi/valuefooter%> diff --git a/modules/luci-base/luasrc/view/cbi/wireless_modefreq.htm b/modules/luci-base/luasrc/view/cbi/wireless_modefreq.htm index ebb02e489b..eeb1d5c5cb 100644 --- a/modules/luci-base/luasrc/view/cbi/wireless_modefreq.htm +++ b/modules/luci-base/luasrc/view/cbi/wireless_modefreq.htm @@ -4,6 +4,7 @@ var freqlist = <%= luci.http.write_json(self.iwinfo.freqlist) %>; var hwmodes = <%= luci.http.write_json(self.iwinfo.hwmodelist or {}) %>; var htmodes = <%= luci.http.write_json(self.iwinfo.htmodelist) %>; + var acs = <%= luci.http.write_json(self.hostapd_acs or 0) %>; var channels = { '11g': [ @@ -14,6 +15,10 @@ ] }; + if (acs < 1) { + channels[(freqlist[freqlist.length - 1].mhz > 2484) ? '11a' : '11g'].length = 0; + } + for (var i = 0; i < freqlist.length; i++) channels[(freqlist[i].mhz > 2484) ? '11a' : '11g'].push( freqlist[i].channel, diff --git a/modules/luci-base/luasrc/view/header.htm b/modules/luci-base/luasrc/view/header.htm index f6e20c9a40..d68e39f917 100644 --- a/modules/luci-base/luasrc/view/header.htm +++ b/modules/luci-base/luasrc/view/header.htm @@ -10,3 +10,15 @@ luci.dispatcher.context.template_header_sent = true end %> + +<script type="text/javascript" src="<%=resource%>/luci.js"></script> +<script type="text/javascript"> + L = new LuCI(<%= luci.http.write_json({ + token = token, + resource = resource, + scriptname = luci.http.getenv("SCRIPT_NAME"), + pathinfo = luci.http.getenv("PATH_INFO"), + requestpath = luci.dispatcher.context.requestpath, + pollinterval = luci.config.main.pollinterval or 5 + }) %>); +</script> diff --git a/modules/luci-base/luasrc/view/lease_status.htm b/modules/luci-base/luasrc/view/lease_status.htm index 658bcfc1bb..bbaf5986ba 100644 --- a/modules/luci-base/luasrc/view/lease_status.htm +++ b/modules/luci-base/luasrc/view/lease_status.htm @@ -1,5 +1,5 @@ <script type="text/javascript">//<![CDATA[ - XHR.poll(5, '<%=url('admin/dhcplease_status')%>', null, + XHR.poll(-1, '<%=url('admin/dhcplease_status')%>', null, function(x, st) { var tb = document.getElementById('lease_status_table'); diff --git a/modules/luci-base/luasrc/view/wifi_assoclist.htm b/modules/luci-base/luasrc/view/wifi_assoclist.htm index 700d998ad8..f6f66fbbc6 100644 --- a/modules/luci-base/luasrc/view/wifi_assoclist.htm +++ b/modules/luci-base/luasrc/view/wifi_assoclist.htm @@ -1,4 +1,21 @@ +<% + local supports_deauth = {} + + local _, v + for _, v in ipairs(luci.util.ubus()) do + local iface = v:match("^hostapd%.(.+)$") + if iface then + local funcs = luci.util.ubus(v) + if type(funcs) == "table" and funcs.del_client then + supports_deauth[iface] = true + end + end + end +%> + <script type="text/javascript">//<![CDATA[ + var supports_deauth = <%= luci.http.write_json(supports_deauth) %>; + function wifirate(bss, rx) { var p = rx ? 'rx_' : 'tx_', s = '%.1f <%:Mbit/s%>, %d<%:MHz%>' @@ -17,7 +34,17 @@ return s; } - XHR.poll(5, '<%=url('admin/wireless_assoclist')%>', null, + function handleDeauth(ev) { + (new XHR()).post('<%=url('admin/wireless_deauth')%>', { + token: '<%=token%>', + iface: ev.target.getAttribute('data-iface'), + bssid: ev.target.getAttribute('data-bssid') + }, function() { + ev.target.disabled = true; + }); + } + + XHR.poll(-1, '<%=url('admin/wireless_assoclist')%>', null, function(x, st) { var tb = document.getElementById('wifi_assoclist_table'); @@ -58,7 +85,15 @@ E('span', wifirate(bss, true)), E('br'), E('span', wifirate(bss, false)) - ]) + ]), + supports_deauth[bss.ifname] ? E('input', { + type: 'button', + class: 'cbi-button cbi-button-remove', + value: '<%:Disconnect%>', + 'data-bssid': bss.bssid, + 'data-iface': bss.ifname, + click: handleDeauth + }) : '-' ]); }); @@ -75,6 +110,9 @@ <div class="th nowrap"><%:Host%></div> <div class="th nowrap"><%:Signal%> / <%:Noise%></div> <div class="th nowrap"><%:RX Rate%> / <%:TX Rate%></div> + <% if next(supports_deauth) then %> + <div class="th right"><%:Disconnect%></div> + <% end %> </div> <div class="tr placeholder"> <div class="td"><em><%:Collecting data...%></em></div> |