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 = util.class
local instanceof = util.instanceof
-- 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") or
- luci.fs.stat(cbidir..cbimap..".lua.gz"),
- "Model not found!")
-
- local func, err = loadfile(cbimap)
- if not func then
- func, err = loadfile(cbidir..cbimap..".lua") or
- loadfile(cbidir..cbimap..".lua.gz")
+ local func, err
+
+ if fs.access(cbimap) then
+ func, err = loadfile(cbimap)
+ elseif 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")
+ else
+ func, err = nil, "Model '" .. cbimap .. "' not found!"
end
+
assert(func, err)
luci.i18n.loadc("cbi")
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.apply_on_parse = nil
self.readinput = true
self.proceed = false
+ self.flow = {}
self.uci = uci.cursor()
self.save = true
for i, config in ipairs(self.parsechain) do
self.uci:save(config)
end
- if self:submitstate() and not self.proceed and (self.flow.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)
self.defaultpath = {}
self.pageaction = false
self.readinput = true
+ self.allow_reset = false
+ self.allow_cancel = false
self.allow_back = false
self.allow_finish = false
self.template = "cbi/delegator"
if type(node) == "table" and getmetatable(node) == nil then
node = Compound(unpack(node))
end
- assert(instanceof(node, Compound), "Invalid node")
+ assert(type(node) == "function" or instanceof(node, Compound), "Invalid")
assert(not self.nodes[name], "Duplicate entry")
self.nodes[name] = node
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, ...)
+ if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then
+ if self.on_cancel then
+ self:on_cancel()
+ return FORM_DONE
+ end
+ end
+
+ if self.on_init and not Map.formvalue(self, "cbi.delg.current") then
+ self:on_init()
+ end
+
local newcurrent
- self.chain = self:get_chain()
- self.current = self:get_active()
- self.active = self:get(self.current)
+ 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")
- self.active:populate_delegator(self)
- if self.active:parse() > FORM_PROCEED then
+ 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
+ elseif stat < FORM_PROCEED then
+ return stat
end
+
- if not newcurrent or not self:get(newcurrent) then
+ if not Map.formvalue(self, "cbi.submit") then
+ return FORM_NODATA
+ elseif not newcurrent or not self:get(newcurrent) then
+ if self.on_done then
+ self:on_done()
+ end
return FORM_DONE
else
self.current = newcurrent
self.active = self:get(self.current)
- self.active:parse(false)
- return FROM_PROCEED
+ if type(self.active) ~= "function" then
+ self.active:parse(false)
+ return FROM_PROCEED
+ else
+ return self:parse(...)
+ end
end
end
self.dynamic = false
end
+-- Define a tab for the section
+function AbstractSection.tab(self, tab, title, desc)
+ self.tabs = self.tabs or { }
+ self.tab_names = self.tab_names or { }
+
+ self.tab_names[#self.tab_names+1] = tab
+ self.tabs[tab] = {
+ title = title,
+ description = desc,
+ childs = { }
+ }
+end
+
-- Appends a new option
function AbstractSection.option(self, class, option, ...)
-- Autodetect from UVL
end
end
+-- Appends a new tabbed option
+function AbstractSection.taboption(self, tab, ...)
+
+ assert(tab and self.tabs and self.tabs[tab],
+ "Cannot assign option to not existing tab %q" % tostring(tab))
+
+ local l = self.tabs[tab].childs
+ local o = AbstractSection.option(self, ...)
+
+ if o then l[#l+1] = o end
+
+ return o
+end
+
+-- Render a single tab
+function AbstractSection.render_tab(self, tab, ...)
+
+ assert(tab and self.tabs and self.tabs[tab],
+ "Cannot render not existing tab %q" % tostring(tab))
+
+ for _, node in ipairs(self.tabs[tab].childs) do
+ node:render(...)
+ end
+end
+
-- Parse optional options
function AbstractSection.parse_optionals(self, section)
if not self.optional then
if v.optional and not v:cfgvalue(section) then
if field == v.option then
field = nil
- else
self.map.proceed = true
+ else
table.insert(self.optionals[section], v)
end
end
local stat
if section then
- stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
+ stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
else
section = self.map:add(self.sectiontype)
stat = section
local fvalue = self:formvalue(section)
local cvalue = self:cfgvalue(section)
+ -- If favlue and cvalue are both tables and have the same content
+ -- make them identical
+ if type(fvalue) == "table" and type(cvalue) == "table" then
+ local equal = #fvalue == #cvalue
+ if equal then
+ for i=1, #fvalue do
+ if cvalue[i] ~= fvalue[i] then
+ equal = false
+ end
+ end
+ end
+ if equal then
+ fvalue = cvalue
+ end
+ end
+
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 and not novld then
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
scope.section = s
scope.cbid = self:cbid(s)
scope.striptags = luci.util.striptags
+ scope.pcdata = luci.util.pcdata
scope.ifattr = function(cond,key,val)
if cond then
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