X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=libs%2Fcbi%2Fluasrc%2Fcbi.lua;h=7dabe2d6002a9fc84105c1fca7fad037787e66dc;hb=ccf73ec14ea31673250ac28d5b51883b474d231e;hp=8da7bc7263f6d0de4d8497e47602f28c4ea78841;hpb=451eacf8da90980f9b860db445cb0b04558e9d31;p=project%2Fluci.git diff --git a/libs/cbi/luasrc/cbi.lua b/libs/cbi/luasrc/cbi.lua index 8da7bc7263..7dabe2d600 100644 --- a/libs/cbi/luasrc/cbi.lua +++ b/libs/cbi/luasrc/cbi.lua @@ -29,10 +29,9 @@ module("luci.cbi", package.seeall) require("luci.template") require("luci.util") require("luci.http") -require("luci.model.uci") require("luci.uvl") -local uci = luci.model.uci +local uci = require("luci.model.uci") local class = luci.util.class local instanceof = luci.util.instanceof @@ -40,6 +39,8 @@ FORM_NODATA = 0 FORM_VALID = 1 FORM_INVALID = -1 +AUTO = true + CREATE_PREFIX = "cbi.cts." REMOVE_PREFIX = "cbi.rts." @@ -77,6 +78,21 @@ function load(cbimap, ...) return maps end + +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() @@ -158,21 +174,24 @@ function Map.__init__(self, config, ...) self.config = config self.parsechain = {self.config} self.template = "cbi/map" - if not uci.load_config(self.config) then + self.uci = uci.cursor() + self.save = true + 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.render(self, ...) - if self.stateful then - uci.load_state(self.config) +function Map.get_scheme(self, sectiontype, option) + if not option then + return self.scheme and self.scheme.sections[sectiontype] else - uci.load_config(self.config) + return self.scheme and self.scheme.variables[sectiontype] + and self.scheme.variables[sectiontype][option] end - Node.render(self, ...) end @@ -183,34 +202,28 @@ end -- Use optimized UCI writing function Map.parse(self, ...) - if self.stateful then - uci.load_state(self.config) - else - uci.load_config(self.config) - end - Node.parse(self, ...) - - for i, config in ipairs(self.parsechain) do - uci.save_config(config) - end - if luci.http.formvalue("cbi.apply") then + + if self.save then for i, config in ipairs(self.parsechain) do - uci.commit(config) - if luci.config.uci_oncommit and luci.config.uci_oncommit[config] then - luci.util.exec(luci.config.uci_oncommit[config]) + self.uci:save(config) + end + if luci.http.formvalue("cbi.apply") then + for i, config in ipairs(self.parsechain) do + self.uci:commit(config) + self.uci:apply(config) + + -- Refresh data because commit changes section names + self.uci:load(config) end - - -- Refresh data because commit changes section names - uci.load_config(config) + + -- Reparse sections + Node.parse(self, ...) + + end + for i, config in ipairs(self.parsechain) do + self.uci:unload(config) end - - -- Reparse sections - Node.parse(self, ...) - - end - for i, config in ipairs(self.parsechain) do - uci.unload(config) end end @@ -227,35 +240,35 @@ end -- UCI add function Map.add(self, sectiontype) - return uci.add(self.config, sectiontype) + return self.uci:add(self.config, sectiontype) end -- UCI set function Map.set(self, section, option, value) if option then - return uci.set(self.config, section, option, value) + return self.uci:set(self.config, section, option, value) else - return uci.set(self.config, section, value) + return self.uci:set(self.config, section, value) end end -- UCI del function Map.del(self, section, option) if option then - return uci.delete(self.config, section, option) + return self.uci:delete(self.config, section, option) else - return uci.delete(self.config, section) + return self.uci:delete(self.config, section) end end -- UCI get function Map.get(self, section, option) if not section then - return uci.get_all(self.config) + return self.uci:get_all(self.config) elseif option then - return uci.get(self.config, section, option) + return self.uci:get(self.config, section, option) else - return uci.get_all(self.config, section) + return self.uci:get_all(self.config, section) end end @@ -286,17 +299,17 @@ function SimpleForm.parse(self, ...) if luci.http.formvalue("cbi.submit") then Node.parse(self, 1, ...) end - + local valid = true - for k, j in ipairs(self.children) do + for k, j in ipairs(self.children) do for i, v in ipairs(j.children) do - valid = valid + valid = valid and (not v.tag_missing or not v.tag_missing[1]) and (not v.tag_invalid or not v.tag_invalid[1]) end end - - local state = + + local state = not luci.http.formvalue("cbi.submit") and 0 or valid and 1 or -1 @@ -332,9 +345,9 @@ function SimpleForm.field(self, class, ...) if not section then section = self:section(SimpleSection) end - + if instanceof(class, AbstractValue) then - local obj = class(self, ...) + local obj = class(self, section, ...) obj.track_missing = true section:append(obj) return obj @@ -358,6 +371,11 @@ function SimpleForm.get(self, section, option) end +function SimpleForm.get_scheme() + return nil +end + + --[[ AbstractSection @@ -371,6 +389,10 @@ function AbstractSection.__init__(self, map, sectiontype, ...) self.config = map.config self.optionals = {} self.defaults = {} + self.fields = {} + self.tag_error = {} + self.tag_invalid = {} + self.tag_deperror = {} self.optional = true self.addremove = false @@ -379,15 +401,32 @@ 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, option, ...) + 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 + elseif class == true then + error("No valid class was given and autodetection failed.") else - error("class must be a descendent of AbstractValue") + error("class must be a descendant of AbstractValue") end end @@ -461,7 +500,7 @@ end -- Creates the section function AbstractSection.create(self, section) local stat - + if section then stat = self.map:set(section, nil, self.sectiontype) else @@ -499,15 +538,19 @@ function Table.__init__(self, form, data, ...) local datasource = {} datasource.config = "table" self.data = data - + function datasource.get(self, section, option) return data[section] and data[section][option] end - + function datasource.del(...) return true end - + + function datasource.get_scheme() + return nil + end + AbstractSection.__init__(self, datasource, "table", ...) self.template = "cbi/tblsection" self.rowcolors = true @@ -524,11 +567,11 @@ end function Table.cfgsections(self) local sections = {} - + for i, v in luci.util.kspairs(self.data) do table.insert(sections, i) end - + return sections end @@ -542,15 +585,17 @@ 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 self.map.scheme and self.map.scheme.sections[self.sectiontype] then - local vs = self.map.scheme.sections[self.sectiontype] + 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" @@ -561,7 +606,6 @@ function NamedSection.parse(self) local s = self.section local active = self:cfgvalue(s) - if self.addremove then local path = self.config.."."..s if active then -- Remove the section @@ -580,6 +624,28 @@ function NamedSection.parse(self) AbstractSection.parse_dynamic(self, s) if luci.http.formvalue("cbi.submit") then Node.parse(self, s) + + if not self.override_scheme and self.map.scheme then + local co = self.map:get() + local stat, err = self.map.validator:validate_section(self.config, s, co) + if err then + --self.map.save = false + if err.code == luci.uvl.errors.ERR_DEPENDENCY then + self.tag_deperror[s] = true + else + self.tag_invalid[s] = true + end + for i, v in ipairs(err.childs) do + if v.option and self.fields[v.option] then + if v.code == luci.uvl.errors.ERR_DEPENDENCY then + self.fields[v.option].tag_reqerror[s] = true + elseif v.code == luci.uvl.errors.ERR_OPTION then + self.fields[v.option].tag_invalid[s] = true + end + end + end + end + end end AbstractSection.parse_optionals(self, s) end @@ -603,18 +669,20 @@ function TypedSection.__init__(self, map, type, ...) self.anonymous = false -- Use defaults from UVL - if self.map.scheme and self.map.scheme.sections[self.sectiontype] then - local vs = self.map.scheme.sections[self.sectiontype] + 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 function TypedSection.cfgsections(self) local sections = {} - uci.foreach(self.map.config, self.sectiontype, + self.map.uci:foreach(self.map.config, self.sectiontype, function (section) if self:checkscope(section[".name"]) then table.insert(sections, section[".name"]) @@ -651,7 +719,7 @@ function TypedSection.parse(self) self.err_invalid = true end - if name and name:len() > 0 then + if name and #name > 0 then self:create(name) end end @@ -661,19 +729,39 @@ function TypedSection.parse(self) crval = REMOVE_PREFIX .. self.config name = luci.http.formvaluetable(crval) for k,v in pairs(name) do - luci.util.perror(k) - luci.util.perror(self:cfgvalue(k)) - luci.util.perror(self:checkscope(k)) if self:cfgvalue(k) and self:checkscope(k) then self:remove(k) end end end + local co for i, k in ipairs(self:cfgsections()) do AbstractSection.parse_dynamic(self, k) if luci.http.formvalue("cbi.submit") then Node.parse(self, k) + + if not self.override_scheme and self.map.scheme then + local co = self.map:get() + local stat, err = self.map.validator:validate_section(self.config, k, co) + if err then + --self.map.save = false + if err.code == luci.uvl.errors.ERR_DEPENDENCY then + self.tag_deperror[k] = true + else + self.tag_invalid[k] = true + end + for i, v in ipairs(err.childs) do + if v.option and self.fields[v.option] then + if v.code == luci.uvl.errors.ERR_DEPENDENCY then + self.fields[v.option].tag_reqerror[k] = true + elseif v.code == luci.uvl.errors.ERR_OPTION then + self.fields[v.option].tag_invalid[k] = true + end + end + end + end + end end AbstractSection.parse_optionals(self, k) end @@ -723,13 +811,15 @@ AbstractValue - An abstract Value Type ]]-- AbstractValue = class(Node) -function AbstractValue.__init__(self, map, option, ...) +function AbstractValue.__init__(self, map, section, option, ...) Node.__init__(self, ...) - self.option = option - self.map = map - self.config = map.config + self.section = section + self.option = option + self.map = map + self.config = map.config self.tag_invalid = {} self.tag_missing = {} + self.tag_reqerror = {} self.tag_error = {} self.deps = {} self.cast = "string" @@ -739,6 +829,25 @@ function AbstractValue.__init__(self, map, option, ...) self.default = nil self.size = nil self.optional = false + + -- 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) + self.rmempty = not vs.required + self.cast = (vs.type == "list") and "list" or "string" + self.title = self.title or vs.title + self.description = self.description or vs.descr + + 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 end -- Add a dependencie to another section field @@ -750,7 +859,7 @@ function AbstractValue.depends(self, field, value) else deps = field end - + table.insert(self.deps, {deps=deps, add=""}) end @@ -887,8 +996,8 @@ end -- DummyValue - This does nothing except being there DummyValue = class(AbstractValue) -function DummyValue.__init__(self, map, ...) - AbstractValue.__init__(self, map, ...) +function DummyValue.__init__(self, ...) + AbstractValue.__init__(self, ...) self.template = "cbi/dvalue" self.value = nil end @@ -941,18 +1050,42 @@ ListValue = class(AbstractValue) function ListValue.__init__(self, ...) AbstractValue.__init__(self, ...) self.template = "cbi/lvalue" + self.keylist = {} self.vallist = {} - self.size = 1 self.widget = "select" + + 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.values and not self.override_values then + if self.rmempty or self.optional then + self:value("") + end + for k, v in pairs(vs.values) do + local deps = {} + if not self.override_dependencies + and vs.enum_depends and vs.enum_depends[k] then + for i, dep in ipairs(vs.enum_depends[k]) do + table.insert(deps, _uvl_strip_remote_dependencies(dep)) + end + end + self:value(k, v, unpack(deps)) + end + end + end end function ListValue.value(self, key, val, ...) + if luci.util.contains(self.keylist, key) then + return + end + val = val or key table.insert(self.keylist, tostring(key)) table.insert(self.vallist, tostring(val)) - + for i, deps in ipairs({...}) do table.insert(self.deps, {add = "-"..key, deps=deps}) end @@ -978,6 +1111,7 @@ MultiValue = class(AbstractValue) function MultiValue.__init__(self, ...) AbstractValue.__init__(self, ...) self.template = "cbi/mvalue" + self.keylist = {} self.vallist = {} @@ -994,6 +1128,10 @@ function MultiValue.render(self, ...) end function MultiValue.value(self, key, val) + if luci.util.contains(self.keylist, key) then + return + end + val = val or key table.insert(self.keylist, tostring(key)) table.insert(self.vallist, tostring(val)) @@ -1030,6 +1168,16 @@ function StaticList.__init__(self, ...) MultiValue.__init__(self, ...) self.cast = "table" self.valuelist = self.cfgvalue + + 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.values and not self.override_values then + for k, v in pairs(vs.values) do + self:value(k, v) + end + end + end end function StaticList.validate(self, value) @@ -1051,7 +1199,6 @@ function DynamicList.__init__(self, ...) AbstractValue.__init__(self, ...) self.template = "cbi/dynlist" self.cast = "table" - self.keylist = {} self.vallist = {} end @@ -1064,7 +1211,7 @@ end function DynamicList.validate(self, value, section) value = (type(value) == "table") and value or {value} - + local valid = {} for i, v in ipairs(value) do if v and #v > 0 and @@ -1072,7 +1219,7 @@ function DynamicList.validate(self, value, section) table.insert(valid, v) end end - + return valid end @@ -1098,4 +1245,4 @@ function Button.__init__(self, ...) self.template = "cbi/button" self.inputstyle = nil self.rmempty = true -end \ No newline at end of file +end