require("luci.util")
require("luci.http")
require("luci.uvl")
+require("luci.fs")
local uci = require("luci.model.uci")
local class = luci.util.class
FORM_NODATA = 0
FORM_VALID = 1
FORM_INVALID = -1
+FORM_CHANGED = 2
AUTO = true
require("luci.config")
require("luci.util")
+ local upldir = "/lib/uci/upload/"
local cbidir = luci.util.libpath() .. "/model/cbi/"
local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
assert(func, err)
return rawget(tbl, key) or _M[key] or _G[key]
end}))
- local maps = {func()}
+ local maps = { func() }
+ local uploads = { }
+ local has_upload = false
for i, map in ipairs(maps) do
if not instanceof(map, Node) then
error("CBI map returns no valid map object!")
return nil
+ else
+ map:prepare()
+ if map.upload_fields then
+ has_upload = true
+ for _, field in ipairs(map.upload_fields) do
+ uploads[
+ field.config .. '.' ..
+ field.section.sectiontype .. '.' ..
+ field.option
+ ] = true
+ end
+ end
end
end
+ if has_upload then
+ local uci = luci.model.uci.cursor()
+ local prm = luci.http.context.request.message.params
+ local fd, cbid
+
+ luci.http.setfilehandler(
+ function( field, chunk, eof )
+ if not field then return end
+ if field.name and not cbid then
+ local c, s, o = field.name:gmatch(
+ "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
+ )()
+
+ if c and s and o then
+ local t = uci:get( c, s )
+ if t and uploads[c.."."..t.."."..o] then
+ local path = upldir .. field.name
+ fd = io.open(path, "w")
+ if fd then
+ cbid = field.name
+ prm[cbid] = path
+ end
+ end
+ end
+ end
+
+ if field.name == cbid and fd then
+ fd:write(chunk)
+ end
+
+ if eof and fd then
+ fd:close()
+ fd = nil
+ cbid = nil
+ end
+ end
+ )
+ end
+
return maps
end
local function tag_fields(e)
if e.option and node.fields[e.option] then
- node.fields[e.option].error = e
+ 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
table.insert( s, c:string() )
end
end
- if #s > 0 then node.error = s 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)
end
end
+-- Prepare nodes
+function Node.prepare(self, ...)
+ for k, child in ipairs(self.children) do
+ child:prepare(...)
+ end
+end
+
-- Append child nodes
function Node.append(self, obj)
table.insert(self.children, obj)
end
end
+function Map.submitstate(self)
+ return luci.http.formvalue("cbi.submit")
+end
-- Chain foreign config
function Map.chain(self, config)
end
-- Use optimized UCI writing
-function Map.parse(self, ...)
- Node.parse(self, ...)
+function Map.parse(self)
+ Node.parse(self)
if self.save then
for i, config in ipairs(self.parsechain) do
self.uci:save(config)
end
- if luci.http.formvalue("cbi.apply") then
+ if self:submitstate() and (self.autoapply or luci.http.formvalue("cbi.apply")) then
for i, config in ipairs(self.parsechain) do
self.uci:commit(config)
end
-- Reparse sections
- Node.parse(self, ...)
+ Node.parse(self, true)
end
for i, config in ipairs(self.parsechain) do
self.uci:unload(config)
end
+ if type(self.commit_handler) == "function" then
+ self:commit_handler(self:submitstate())
+ end
+ end
+
+ if self:submitstate() then
+ if self.save then
+ self.state = self.changed and FORM_CHANGED or FORM_VALID
+ else
+ self.state = FORM_INVALID
+ end
+ else
+ self.state = FORM_NODATA
end
+
+ return self.state
end
function Map.render(self, ...)
self.data = data or {}
self.template = "cbi/simpleform"
self.dorender = true
+ self.pageaction = false
end
function SimpleForm.parse(self, ...)
end
local state =
- not luci.http.formvalue("cbi.submit") and 0
- or valid and 1
- or -1
+ not self:submitstate() and FORM_NODATA
+ or valid and FORM_VALID
+ or FORM_INVALID
self.dorender = not self.handle or self:handle(state, self.data) ~= false
+ return state
end
function SimpleForm.render(self, ...)
end
end
+function SimpleForm.submitstate(self)
+ return luci.http.formvalue("cbi.submit")
+end
+
function SimpleForm.section(self, class, ...)
if instanceof(class, AbstractSection) then
local obj = class(self, ...)
local stat
if section then
- stat = 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
function datasource.get(self, section, option)
return data[section] and data[section][option]
end
+
+ function datasource.submitstate(self)
+ return luci.http.formvalue("cbi.submit")
+ end
function datasource.del(...)
return true
function Table.parse(self)
for i, k in ipairs(self:cfgsections()) do
- if luci.http.formvalue("cbi.submit") then
+ if self.map:submitstate() then
Node.parse(self, k)
end
end
self.section = section
end
-function NamedSection.parse(self)
+function NamedSection.parse(self, novld)
local s = self.section
local active = self:cfgvalue(s)
if active then
AbstractSection.parse_dynamic(self, s)
- if luci.http.formvalue("cbi.submit") then
+ if self.map:submitstate() then
Node.parse(self, s)
- if not self.override_scheme and self.map.scheme then
+ if not novld and not self.override_scheme and self.map.scheme then
_uvl_validate_section(self, s)
end
end
table.insert(self.deps, {option=option, value=value})
end
-function TypedSection.parse(self)
+function TypedSection.parse(self, novld)
if self.addremove then
-- Remove
local crval = REMOVE_PREFIX .. self.config
local co
for i, k in ipairs(self:cfgsections()) do
AbstractSection.parse_dynamic(self, k)
- if luci.http.formvalue("cbi.submit") then
+ if self.map:submitstate() then
Node.parse(self, k)
- if not self.override_scheme and self.map.scheme then
+ if not novld and not self.override_scheme and self.map.scheme then
_uvl_validate_section(self, k)
end
end
if name and #name > 0 then
created = self:create(name) and name
+ if not created then
+ self.invalid_cts = true
+ end
end
end
end
self.tag_reqerror = {}
self.tag_error = {}
self.deps = {}
- self.cast = "string"
+ --self.cast = "string"
self.track_missing = false
- self.rmempty = false
+ --self.rmempty = false
self.default = nil
self.size = nil
self.optional = false
+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)
- self.rmempty = not vs.required
- self.cast = (vs.type == "list") and "list" or "string"
+ 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.title = self.title or vs.title
self.description = self.description or vs.descr
- self.default = vs.default
+ 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
end
end
end
+
+ self.cast = self.cast or "string"
end
-- Add a dependencie to another section field
local fvalue = self:formvalue(section)
local cvalue = self:cfgvalue(section)
- if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
+ 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
-- Return the UCI value of this object
function AbstractValue.cfgvalue(self, section)
local value = self.map:get(section, self.option)
- if not self.cast or self.cast == type(value) then
+ if not value then
+ return nil
+ elseif not self.cast or self.cast == type(value) then
return value
elseif self.cast == "string" then
if type(value) == "table" then
return value[1]
end
elseif self.cast == "table" then
- return {value}
+ return luci.util.split(value, "%s+", nil, true)
end
end
self.value = nil
end
+function DummyValue.cfgvalue(self, section)
+ local value
+ if self.value then
+ if type(self.value) == "function" then
+ value = self:value(section)
+ else
+ value = self.value
+ end
+ else
+ value = AbstractValue.cfgvalue(self, section)
+ end
+ return value
+end
+
function DummyValue.parse(self)
end
self.vallist = {}
self.size = 1
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.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
+ 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[k] then
- for i, dep in ipairs(vs.enum_depends[k]) do
+ 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(k, v, unpack(deps))
+ self:value(v.value, v.title or v.value, unpack(deps))
end
end
end
local valid = {}
for i, v in ipairs(value) do
- if luci.util.contains(self.valuelist, v) then
+ if luci.util.contains(self.vallist, v) then
table.insert(valid, v)
end
end
table.insert(self.vallist, tostring(val))
end
-function DynamicList.validate(self, value, section)
+function DynamicList.formvalue(self, section)
+ local value = AbstractValue.formvalue(self, section)
value = (type(value) == "table") and value or {value}
local valid = {}
self.inputstyle = nil
self.rmempty = true
end
+
+
+FileUpload = class(AbstractValue)
+
+function FileUpload.__init__(self, ...)
+ AbstractValue.__init__(self, ...)
+ self.template = "cbi/upload"
+ if not self.map.upload_fields then
+ self.map.upload_fields = { self }
+ else
+ self.map.upload_fields[#self.map.upload_fields+1] = self
+ end
+end
+
+function FileUpload.formcreated(self, section)
+ return AbstractValue.formcreated(self, section) or
+ luci.http.formvalue("cbi.rlf."..section.."."..self.option) or
+ luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
+end
+
+function FileUpload.cfgvalue(self, section)
+ local val = AbstractValue.cfgvalue(self, section)
+ if val and luci.fs.access(val) then
+ return val
+ end
+ return nil
+end
+
+function FileUpload.formvalue(self, section)
+ local val = AbstractValue.formvalue(self, section)
+ if val then
+ if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
+ not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
+ then
+ return val
+ end
+ luci.fs.unlink(val)
+ self.value = nil
+ end
+ return nil
+end
+
+function FileUpload.remove(self, section)
+ local val = AbstractValue.formvalue(self, section)
+ if val and luci.fs.access(val) then luci.fs.unlink(val) end
+ return AbstractValue.remove(self, section)
+end
+
+
+FileBrowser = class(AbstractValue)
+
+function FileBrowser.__init__(self, ...)
+ AbstractValue.__init__(self, ...)
+ self.template = "cbi/browser"
+end