X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fcbi%2Fluasrc%2Fcbi.lua;h=8dd16b181eb67ee7cd5519248ae15f5eba18c8ff;hp=7c20129be8a1a64404a67b638fefda8a1aa6e087;hb=f735ef30bdf397b0b590f89cc5c77e7783232ca5;hpb=e93a3e0c749960deefdde6fb3d0e1a1ee3696b89 diff --git a/libs/cbi/luasrc/cbi.lua b/libs/cbi/luasrc/cbi.lua index 7c20129be..8dd16b181 100644 --- a/libs/cbi/luasrc/cbi.lua +++ b/libs/cbi/luasrc/cbi.lua @@ -29,12 +29,12 @@ module("luci.cbi", package.seeall) require("luci.template") local util = require("luci.util") require("luci.http") -require("luci.uvl") --local event = require "luci.sys.event" local fs = require("nixio.fs") local uci = require("luci.model.uci") +local datatypes = require("luci.cbi.datatypes") local class = util.class local instanceof = util.instanceof @@ -62,20 +62,17 @@ function load(cbimap, ...) local cbidir = luci.util.libpath() .. "/model/cbi/" local func, err - if fs.access(cbimap) then - func, err = loadfile(cbimap) - elseif fs.access(cbidir..cbimap..".lua") then + if fs.access(cbidir..cbimap..".lua") then func, err = loadfile(cbidir..cbimap..".lua") - elseif fs.access(cbidir..cbimap..".lua.gz") then - func, err = loadfile(cbidir..cbimap..".lua.gz") + elseif fs.access(cbimap) then + func, err = loadfile(cbimap) else func, err = nil, "Model '" .. cbimap .. "' not found!" end assert(func, err) - luci.i18n.loadc("cbi") - luci.i18n.loadc("uvl") + luci.i18n.loadc("base") local env = { translate=i18n.translate, @@ -153,64 +150,6 @@ function load(cbimap, ...) return maps end -local function _uvl_validate_section(node, name) - local co = node.map:get() - - luci.uvl.STRICT_UNKNOWN_OPTIONS = false - luci.uvl.STRICT_UNKNOWN_SECTIONS = false - - local function tag_fields(e) - if e.option and node.fields[e.option] then - if node.fields[e.option].error then - node.fields[e.option].error[name] = e - else - node.fields[e.option].error = { [name] = e } - end - elseif e.childs then - for _, c in ipairs(e.childs) do tag_fields(c) end - end - end - - local function tag_section(e) - local s = { } - for _, c in ipairs(e.childs or { e }) do - if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then - table.insert( s, c.childs[1]:string() ) - else - table.insert( s, c:string() ) - end - end - if #s > 0 then - if node.error then - node.error[name] = s - else - node.error = { [name] = s } - end - end - end - - local stat, err = node.map.validator:validate_section(node.config, name, co) - if err then - node.map.save = false - tag_fields(err) - tag_section(err) - end - -end - -local function _uvl_strip_remote_dependencies(deps) - local clean = {} - - for k, v in pairs(deps) do - k = k:gsub("%$config%.%$section%.", "") - if k:match("^[%w_]+$") and type(v) == "string" then - clean[k] = v - end - end - - return clean -end - -- Node pseudo abstract class Node = class() @@ -222,23 +161,13 @@ function Node.__init__(self, title, description) self.template = "cbi/node" end --- i18n helper -function Node._i18n(self, config, section, option, title, description) - - -- i18n loaded? - if type(luci.i18n) == "table" then - - local key = config and config:gsub("[^%w]+", "") or "" - - if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end - if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end - - self.title = title or luci.i18n.translate( key, option or section or config ) - self.description = description or luci.i18n.translate( key .. "_desc", "" ) +-- hook helper +function Node._run_hook(self, hook) + if type(self[hook]) == "function" then + return self[hook](self) end end --- hook helper function Node._run_hooks(self, ...) local f local r = false @@ -313,7 +242,6 @@ Map = class(Node) function Map.__init__(self, config, ...) Node.__init__(self, ...) - Node._i18n(self, config, nil, nil, ...) self.config = config self.parsechain = {self.config} @@ -331,9 +259,6 @@ function Map.__init__(self, config, ...) if not self.uci:load(self.config) then error("Unable to read UCI data: " .. self.config) end - - self.validator = luci.uvl.UVL() - self.scheme = self.validator:get_scheme(self.config) end function Map.formvalue(self, key) @@ -369,6 +294,7 @@ end -- Use optimized UCI writing function Map.parse(self, readinput, ...) self.readinput = (readinput ~= false) + self:_run_hooks("on_parse") if self:formvalue("cbi.skip") then self.state = FORM_SKIP @@ -430,7 +356,7 @@ end function Map.render(self, ...) self:_run_hooks("on_init") Node.render(self, ...) - if self._apply then + if false and self._apply then local fp = self._apply() fp:read("*a") fp:close() @@ -530,10 +456,6 @@ function Delegator.__init__(self, ...) end function Delegator.set(self, name, node) - if type(node) == "table" and getmetatable(node) == nil then - node = Compound(unpack(node)) - end - assert(type(node) == "function" or instanceof(node, Compound), "Invalid") assert(not self.nodes[name], "Duplicate entry") self.nodes[name] = node @@ -545,9 +467,9 @@ function Delegator.add(self, name, node) end function Delegator.insert_after(self, name, after) - local n = #self.chain + local n = #self.chain + 1 for k, v in ipairs(self.chain) do - if v == state then + if v == after then n = k + 1 break end @@ -573,7 +495,17 @@ function Delegator.set_route(self, ...) end function Delegator.get(self, name) - return self.nodes[name] + local node = self.nodes[name] + + if type(node) == "string" then + node = load(node, name) + end + + if type(node) == "table" and getmetatable(node) == nil then + node = Compound(unpack(node)) + end + + return node end function Delegator.parse(self, ...) @@ -582,7 +514,7 @@ function Delegator.parse(self, ...) return FORM_DONE end end - + if not Map.formvalue(self, "cbi.delg.current") then self:_run_hooks("on_init") end @@ -592,11 +524,11 @@ function Delegator.parse(self, ...) self.current = self.current or self:get_active() self.active = self.active or self:get(self.current) assert(self.active, "Invalid state") - + local stat = FORM_DONE if type(self.active) ~= "function" then self.active:populate_delegator(self) - stat = self.active:parse() + stat = self.active:parse() else self:active() end @@ -610,20 +542,24 @@ function Delegator.parse(self, ...) elseif stat < FORM_PROCEED then return stat end - + if not Map.formvalue(self, "cbi.submit") then return FORM_NODATA - elseif stat > FORM_PROCEED + elseif stat > FORM_PROCEED and (not newcurrent or not self:get(newcurrent)) then - self:_run_hooks("on_done") - return FORM_DONE + return self:_run_hook("on_done") or FORM_DONE else self.current = newcurrent or self.current self.active = self:get(self.current) if type(self.active) ~= "function" then - self.active:parse(false) - return FROM_PROCEED + self.active:populate_delegator(self) + local stat = self.active:parse(false) + if stat == FORM_SKIP then + return self:parse(...) + else + return FORM_PROCEED + end else return self:parse(...) end @@ -689,6 +625,10 @@ function SimpleForm.parse(self, readinput, ...) return FORM_SKIP end + if self:formvalue("cbi.cancel") and self:_run_hooks("on_cancel") then + return FORM_DONE + end + if self:submitstate() then Node.parse(self, 1, ...) end @@ -824,27 +764,15 @@ function AbstractSection.tab(self, tab, title, desc) } end +-- Check whether the section has tabs +function AbstractSection.has_tabs(self) + return (self.tabs ~= nil) and (next(self.tabs) ~= nil) +end + -- Appends a new option function AbstractSection.option(self, class, option, ...) - -- Autodetect from UVL - if class == true and self.map:get_scheme(self.sectiontype, option) then - local vs = self.map:get_scheme(self.sectiontype, option) - if vs.type == "boolean" then - class = Flag - elseif vs.type == "list" then - class = DynamicList - elseif vs.type == "enum" or vs.type == "reference" then - class = ListValue - else - class = Value - end - end - if instanceof(class, AbstractValue) then local obj = class(self.map, self, option, ...) - - Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...) - self:append(obj) self.fields[option] = obj return obj @@ -890,7 +818,7 @@ function AbstractSection.parse_optionals(self, section) local field = self.map:formvalue("cbi.opt."..self.config.."."..section) for k,v in ipairs(self.children) do - if v.optional and not v:cfgvalue(section) then + if v.optional and not v:cfgvalue(section) and not self:has_tabs() then if field == v.option then field = nil self.map.proceed = true @@ -1059,20 +987,9 @@ NamedSection = class(AbstractSection) function NamedSection.__init__(self, map, section, stype, ...) AbstractSection.__init__(self, map, stype, ...) - Node._i18n(self, map.config, section, nil, ...) -- Defaults self.addremove = false - - -- Use defaults from UVL - if not self.override_scheme and self.map:get_scheme(self.sectiontype) then - local vs = self.map:get_scheme(self.sectiontype) - self.addremove = not vs.unique and not vs.required - self.dynamic = vs.dynamic - self.title = self.title or vs.title - self.description = self.description or vs.descr - end - self.template = "cbi/nsection" self.section = section end @@ -1100,10 +1017,6 @@ function NamedSection.parse(self, novld) AbstractSection.parse_dynamic(self, s) if self.map:submitstate() then Node.parse(self, s) - - if not novld and not self.override_scheme and self.map.scheme then - _uvl_validate_section(self, s) - end end AbstractSection.parse_optionals(self, s) @@ -1124,21 +1037,10 @@ TypedSection = class(AbstractSection) function TypedSection.__init__(self, map, type, ...) AbstractSection.__init__(self, map, type, ...) - Node._i18n(self, map.config, type, nil, ...) - self.template = "cbi/tsection" + self.template = "cbi/tsection" self.deps = {} self.anonymous = false - - -- Use defaults from UVL - if not self.override_scheme and self.map:get_scheme(self.sectiontype) then - local vs = self.map:get_scheme(self.sectiontype) - self.addremove = not vs.unique and not vs.required - self.dynamic = vs.dynamic - self.anonymous = not vs.named - self.title = self.title or vs.title - self.description = self.description or vs.descr - end end -- Return all matching UCI sections for this TypedSection @@ -1179,10 +1081,6 @@ function TypedSection.parse(self, novld) AbstractSection.parse_dynamic(self, k) if self.map:submitstate() then Node.parse(self, k, novld) - - if not novld and not self.override_scheme and self.map.scheme then - _uvl_validate_section(self, k) - end end AbstractSection.parse_optionals(self, k) end @@ -1283,6 +1181,7 @@ function AbstractValue.__init__(self, map, section, option, ...) self.tag_reqerror = {} self.tag_error = {} self.deps = {} + self.subdeps = {} --self.cast = "string" self.track_missing = false @@ -1293,29 +1192,6 @@ function AbstractValue.__init__(self, map, section, option, ...) end function AbstractValue.prepare(self) - -- Use defaults from UVL - if not self.override_scheme - and self.map:get_scheme(self.section.sectiontype, self.option) then - local vs = self.map:get_scheme(self.section.sectiontype, self.option) - if self.cast == nil then - self.cast = (vs.type == "list") and "list" or "string" - end - self.title = self.title or vs.title - self.description = self.description or vs.descr - if self.default == nil then - self.default = vs.default - end - - if vs.depends and not self.override_dependencies then - for i, deps in ipairs(vs.depends) do - deps = _uvl_strip_remote_dependencies(deps) - if next(deps) then - self:depends(deps) - end - end - end - end - self.cast = self.cast or "string" end @@ -1388,7 +1264,7 @@ function AbstractValue.parse(self, section, novld) table.insert(self.section.error[section], "invalid") else self.section.error = {[section] = {"invalid"}} - end + end self.map.save = false end if fvalue and not (fvalue == cvalue) then @@ -1419,7 +1295,7 @@ end -- Render if this value exists or if it is mandatory function AbstractValue.render(self, s, scope) - if not self.optional or self:cfgvalue(s) or self:formcreated(s) then + if not self.optional or self.section:has_tabs() or self:cfgvalue(s) or self:formcreated(s) then scope = scope or {} scope.section = s scope.cbid = self:cbid(s) @@ -1450,7 +1326,8 @@ end -- Return the UCI value of this object function AbstractValue.cfgvalue(self, section) - local value = self.map:get(section, self.option) + local value = (self.error and self.error[section] == "invalid") + and self:formvalue(section) or self.map:get(section, self.option) if not value then return nil elseif not self.cast or self.cast == type(value) then @@ -1466,6 +1343,20 @@ end -- Validate the form value function AbstractValue.validate(self, value) + if self.datatype and value and datatypes[self.datatype] then + if type(value) == "table" then + local v + for _, v in ipairs(value) do + if v and #v > 0 and not datatypes[self.datatype](v) then + return nil + end + end + else + if not datatypes[self.datatype](value) then + return nil + end + end + end return value end @@ -1583,26 +1474,6 @@ function ListValue.__init__(self, ...) self.widget = "select" end -function ListValue.prepare(self, ...) - AbstractValue.prepare(self, ...) - if not self.override_scheme - and self.map:get_scheme(self.section.sectiontype, self.option) then - local vs = self.map:get_scheme(self.section.sectiontype, self.option) - if self.value and vs.valuelist and not self.override_values then - for k, v in ipairs(vs.valuelist) do - local deps = {} - if not self.override_dependencies - and vs.enum_depends and vs.enum_depends[v.value] then - for i, dep in ipairs(vs.enum_depends[v.value]) do - table.insert(deps, _uvl_strip_remote_dependencies(dep)) - end - end - self:value(v.value, v.title or v.value, unpack(deps)) - end - end - end -end - function ListValue.value(self, key, val, ...) if luci.util.contains(self.keylist, key) then return @@ -1613,7 +1484,7 @@ function ListValue.value(self, key, val, ...) table.insert(self.vallist, tostring(val)) for i, deps in ipairs({...}) do - table.insert(self.deps, {add = "-"..key, deps=deps}) + self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps} end end