module("luci.cbi", package.seeall)
require("luci.template")
-require("luci.util")
+local util = require("luci.util")
require("luci.http")
require("luci.uvl")
-require("luci.fs")
+
--local event = require "luci.sys.event"
+local fs = require("nixio.fs")
local uci = require("luci.model.uci")
-local class = luci.util.class
-local instanceof = luci.util.instanceof
+local class = util.class
+local instanceof = util.instanceof
FORM_NODATA = 0
FORM_PROCEED = 0
FORM_VALID = 1
+FORM_DONE = 1
FORM_INVALID = -1
FORM_CHANGED = 2
+FORM_SKIP = 4
AUTO = true
-- Loads a CBI map from given file, creating an environment and returns it
function load(cbimap, ...)
- require("luci.fs")
+ local fs = require "nixio.fs"
local i18n = require "luci.i18n"
require("luci.config")
require("luci.util")
local upldir = "/lib/uci/upload/"
local cbidir = luci.util.libpath() .. "/model/cbi/"
-
- assert(luci.fs.stat(cbimap) or luci.fs.stat(cbidir..cbimap..".lua"),
- "Model not found!")
-
+
+ assert(fs.stat(cbimap) or
+ fs.stat(cbidir..cbimap..".lua") or
+ fs.stat(cbidir..cbimap..".lua.gz"),
+ "Model not found!")
+
local func, err = loadfile(cbimap)
if not func then
- func, err = loadfile(cbidir..cbimap..".lua")
+ func, err = loadfile(cbidir..cbimap..".lua") or
+ loadfile(cbidir..cbimap..".lua.gz")
end
assert(func, err)
local function tag_section(e)
local s = { }
- for _, c in ipairs(e.childs) do
+ 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
luci.template.render(self.template, {self=self})
end
+function Template.parse(self, readinput)
+ self.readinput = (readinput ~= false)
+ return Map.formvalue(self, "cbi.submit") and FORM_DONE or FORM_NODATA
+end
+
--[[
Map - A map describing a configuration file
self.template = "cbi/map"
self.apply_on_parse = nil
self.readinput = true
+ self.proceed = false
+ self.flow = {}
self.uci = uci.cursor()
self.save = true
-
+
self.changed = false
-
+
if not self.uci:load(self.config) then
error("Unable to read UCI data: " .. self.config)
end
end
function Map.formvaluetable(self, key)
- return self.readinput and luci.http.formvaluetable(key)
+ return self.readinput and luci.http.formvaluetable(key) or {}
end
function Map.get_scheme(self, sectiontype, option)
-- Use optimized UCI writing
function Map.parse(self, readinput, ...)
self.readinput = (readinput ~= false)
+
+ if self:formvalue("cbi.skip") then
+ self.state = FORM_SKIP
+ return self:state_handler(self.state)
+ end
+
Node.parse(self, ...)
if self.save then
for i, config in ipairs(self.parsechain) do
self.uci:save(config)
end
- if self:submitstate() and (self.autoapply or luci.http.formvalue("cbi.apply")) then
+ if self:submitstate() and not self.proceed and (self.flow.autoapply or luci.http.formvalue("cbi.apply")) then
for i, config in ipairs(self.parsechain) do
self.uci:commit(config)
end
if self:submitstate() then
- if self.save then
- self.state = self.changed and FORM_CHANGED or FORM_VALID
- else
+ if not self.save then
self.state = FORM_INVALID
+ elseif self.proceed then
+ self.state = FORM_PROCEED
+ else
+ self.state = self.changed and FORM_CHANGED or FORM_VALID
end
else
self.state = FORM_NODATA
function Compound.__init__(self, ...)
Node.__init__(self)
+ self.template = "cbi/compound"
self.children = {...}
end
+function Compound.populate_delegator(self, delegator)
+ for _, v in ipairs(self.children) do
+ v.delegator = delegator
+ end
+end
+
function Compound.parse(self, ...)
- local cstate, state = 0, 0
-
+ local cstate, state = 0
+
for k, child in ipairs(self.children) do
cstate = child:parse(...)
state = (not state or cstate < state) and cstate or state
end
-
+
return state
end
function Delegator.__init__(self, ...)
Node.__init__(self, ...)
self.nodes = {}
+ self.defaultpath = {}
+ self.pageaction = false
+ self.readinput = true
+ self.allow_reset = false
+ self.allow_back = false
+ self.allow_finish = false
self.template = "cbi/delegator"
end
-function Delegator.state(self, name, node, transitor)
- transitor = transitor or self.transistor_linear
- local state = {node=node, name=name, transitor=transitor}
-
- assert(instanceof(node, Node), "Invalid node")
+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] = state
- self:append(state)
-
- return state
+
+ self.nodes[name] = node
end
-function Delegator.get(self, name)
- return self.nodes[name]
+function Delegator.add(self, name, node)
+ node = self:set(name, node)
+ self.defaultpath[#self.defaultpath+1] = name
end
-function Delegator.transistor_linear(self, state, cstate)
- if cstate > 0 then
- for i, child in ipairs(self.children) do
- if state == child then
- return self.children[i+1]
- end
+function Delegator.insert_after(self, name, after)
+ local n = #self.chain
+ for k, v in ipairs(self.chain) do
+ if v == state then
+ n = k + 1
+ break
end
- else
- return state
end
+ table.insert(self.chain, n, name)
+end
+
+function Delegator.set_route(self, ...)
+ local n, chain, route = 0, self.chain, {...}
+ for i = 1, #chain do
+ if chain[i] == self.current then
+ n = i
+ break
+ end
+ end
+ for i = 1, #route do
+ n = n + 1
+ chain[n] = route[i]
+ end
+ for i = n + 1, #chain do
+ chain[i] = nil
+ end
+end
+
+function Delegator.get(self, name)
+ return self.nodes[name]
end
function Delegator.parse(self, ...)
- local active = self:getactive()
- assert(active, "Invalid state")
+ local newcurrent
+ self.chain = self.chain or self:get_chain()
+ self.current = self.current or self:get_active()
+ self.active = self.active or self:get(self.current)
+ assert(self.active, "Invalid state")
- local cstate = active.node:parse()
- self.active = active.transistor(self, active.node, cstate)
+ local stat = FORM_DONE
+ if type(self.active) ~= "function" then
+ self.active:populate_delegator(self)
+ stat = self.active:parse()
+ else
+ self:active()
+ end
+
+ if stat > FORM_PROCEED then
+ if Map.formvalue(self, "cbi.delg.back") then
+ newcurrent = self:get_prev(self.current)
+ else
+ newcurrent = self:get_next(self.current)
+ end
+ end
- if not self.active then
+ if not Map.formvalue(self, "cbi.submit") then
+ return FORM_NODATA
+ elseif not newcurrent or not self:get(newcurrent) then
return FORM_DONE
else
- self.active:parse(false)
- return FROM_PROCEED
+ self.current = newcurrent
+ self.active = self:get(self.current)
+ if type(self.active) ~= "function" then
+ self.active:parse(false)
+ return FROM_PROCEED
+ else
+ return self:parse(...)
+ end
end
end
-function Delegator.render(self, ...)
- self.active.node:render(...)
+function Delegator.get_next(self, state)
+ for k, v in ipairs(self.chain) do
+ if v == state then
+ return self.chain[k+1]
+ end
+ end
end
-function Delegator.getactive(self)
- return self:get(Map.formvalue(self, "cbi.delegated")
- or (self.children[1] and self.children[1].name))
+function Delegator.get_prev(self, state)
+ for k, v in ipairs(self.chain) do
+ if v == state then
+ return self.chain[k-1]
+ end
+ end
+end
+
+function Delegator.get_chain(self)
+ local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
+ return type(x) == "table" and x or {x}
+end
+
+function Delegator.get_active(self)
+ return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
end
--[[
function SimpleForm.parse(self, readinput, ...)
self.readinput = (readinput ~= false)
+
+ if self:formvalue("cbi.skip") then
+ return FORM_SKIP
+ end
+
if self:submitstate() then
Node.parse(self, 1, ...)
end
valid = valid
and (not v.tag_missing or not v.tag_missing[1])
and (not v.tag_invalid or not v.tag_invalid[1])
+ and (not v.error)
end
end
or valid and FORM_VALID
or FORM_INVALID
- self.dorender = not self.handle or self:handle(state, self.data) ~= false
+ self.dorender = not self.handle
+ if self.handle then
+ local nrender, nstate = self:handle(state, self.data)
+ self.dorender = self.dorender or (nrender ~= false)
+ state = nstate or state
+ end
return state
end
end
+Form = class(SimpleForm)
+
+function Form.__init__(self, ...)
+ SimpleForm.__init__(self, ...)
+ self.embedded = true
+end
+
--[[
AbstractSection
if v.optional and not v:cfgvalue(section) then
if field == v.option then
field = nil
+ self.map.proceed = true
else
table.insert(self.optionals[section], v)
end
end
if create and key:sub(1, 1) ~= "." then
+ self.map.proceed = true
self:add_dynamic(key, true)
end
end
-- Removes the section
function AbstractSection.remove(self, section)
+ self.map.proceed = true
return self.map:del(section)
end
end
end
+ self.map.proceed = true
+
return stat
end
function Table.__init__(self, form, data, ...)
local datasource = {}
+ local tself = self
datasource.config = "table"
- self.data = data
-
+ self.data = data or {}
+
datasource.formvalue = Map.formvalue
datasource.formvaluetable = Map.formvaluetable
datasource.readinput = true
function datasource.get(self, section, option)
- return data[section] and data[section][option]
+ return tself.data[section] and tself.data[section][option]
end
-
+
function datasource.submitstate(self)
return Map.formvalue(self, "cbi.submit")
end
return sections
end
+function Table.update(self, data)
+ self.data = data
+end
+
--[[
end
end
AbstractSection.parse_optionals(self, s)
-
+
if self.changed then
self:push_events()
end
for i, k in ipairs(self:cfgsections()) do
AbstractSection.parse_dynamic(self, k)
if self.map:submitstate() then
- Node.parse(self, k)
+ Node.parse(self, k, novld)
if not novld and not self.override_scheme and self.map.scheme then
_uvl_validate_section(self, k)
--self.cast = "string"
self.track_missing = false
- --self.rmempty = false
+ self.rmempty = true
self.default = nil
self.size = nil
self.optional = false
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.rmempty == nil then
- self.rmempty = not vs.required
- end
if self.cast == nil then
self.cast = (vs.type == "list") and "list" or "string"
end
self.rmempty = not value
end
-function AbstractValue.parse(self, section)
+function AbstractValue.parse(self, section, novld)
local fvalue = self:formvalue(section)
local cvalue = self:cfgvalue(section)
if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
fvalue = self:transform(self:validate(fvalue, section))
- if not fvalue then
- self.tag_invalid[section] = true
+ if not fvalue and not novld then
+ if self.error then
+ self.error[section] = "invalid"
+ else
+ self.error = { [section] = "invalid" }
+ end
+ if self.section.error then
+ table.insert(self.section.error[section], "invalid")
+ else
+ self.section.error = {[section] = {"invalid"}}
+ end
+ self.map.save = false
end
if fvalue and not (fvalue == cvalue) then
if self:write(section, fvalue) then
-- Push events
self.section.changed = true
- --luci.util.append(self.map.events, self.events)
+ --luci.util.append(self.map.events, self.events)
end
end
else -- Unset the UCI or error
self.section.changed = true
--luci.util.append(self.map.events, self.events)
end
- elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
- self.tag_missing[section] = true
+ elseif cvalue ~= fvalue and not novld then
+ self:write(section, fvalue or "")
+ if self.error then
+ self.error[section] = "missing"
+ else
+ self.error = { [section] = "missing" }
+ end
+ self.map.save = false
end
end
end
local valid = {}
for i, v in ipairs(value) do
- if luci.util.contains(self.vallist, v) then
+ if luci.util.contains(self.keylist, v) then
table.insert(valid, v)
end
end
table.insert(self.vallist, tostring(val))
end
+function DynamicList.write(self, ...)
+ self.map.proceed = true
+ return AbstractValue.write(self, ...)
+end
+
function DynamicList.formvalue(self, section)
local value = AbstractValue.formvalue(self, section)
value = (type(value) == "table") and value or {value}
function FileUpload.cfgvalue(self, section)
local val = AbstractValue.cfgvalue(self, section)
- if val and luci.fs.access(val) then
+ if val and fs.access(val) then
return val
end
return nil
then
return val
end
- luci.fs.unlink(val)
+ fs.unlink(val)
self.value = nil
end
return nil
function FileUpload.remove(self, section)
local val = AbstractValue.formvalue(self, section)
- if val and luci.fs.access(val) then luci.fs.unlink(val) end
+ if val and fs.access(val) then fs.unlink(val) end
return AbstractValue.remove(self, section)
end