X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fcbi%2Fluasrc%2Fcbi.lua;h=c9db8beb255dd5e201ef29a92d5ad777ecf5879a;hp=296ec815ff14379c6b1d6af51f3f3deee9e92ed5;hb=1f83b916ac83eff03e582084dd99579689570360;hpb=c0b2cda50edbe5bd39e47f776b2bc73e3d175bc4 diff --git a/libs/cbi/luasrc/cbi.lua b/libs/cbi/luasrc/cbi.lua index 296ec815f..c9db8beb2 100644 --- a/libs/cbi/luasrc/cbi.lua +++ b/libs/cbi/luasrc/cbi.lua @@ -27,18 +27,24 @@ limitations under the License. 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 @@ -47,18 +53,28 @@ REMOVE_PREFIX = "cbi.rts." -- 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/" - local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua") + 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.i18n.loadc("uvl") + luci.i18n.loadc("base") local env = { translate=i18n.translate, @@ -121,7 +137,6 @@ function load(cbimap, ...) end if field.name == cbid and fd then - io.stderr:write("*** CHUNK:"..tostring(#chunk).." ***\n") fd:write(chunk) end @@ -157,7 +172,7 @@ local function _uvl_validate_section(node, name) 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 @@ -206,20 +221,23 @@ 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 +-- hook helper +function Node._run_hook(self, hook) + if type(self[hook]) == "function" then + return self[hook](self) + end +end - self.title = title or luci.i18n.translate( key, option or section or config ) - self.description = description or luci.i18n.translate( key .. "_desc", "" ) +function Node._run_hooks(self, ...) + local f + local r = false + for _, f in ipairs(arg) do + if type(self[f]) == "function" then + self[f](self) + r = true + end end + return r end -- Prepare nodes @@ -271,6 +289,11 @@ function Template.render(self) 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 @@ -279,21 +302,34 @@ Map = class(Node) function Map.__init__(self, config, ...) Node.__init__(self, ...) - Node._i18n(self, config, nil, nil, ...) self.config = config self.parsechain = {self.config} 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 self.validator = luci.uvl.UVL() self.scheme = self.validator:get_scheme(self.config) +end + +function Map.formvalue(self, key) + return self.readinput and luci.http.formvalue(key) +end +function Map.formvaluetable(self, key) + return self.readinput and luci.http.formvaluetable(key) or {} end function Map.get_scheme(self, sectiontype, option) @@ -305,29 +341,47 @@ function Map.get_scheme(self, sectiontype, option) end end +function Map.submitstate(self) + return self:formvalue("cbi.submit") +end -- Chain foreign config function Map.chain(self, config) table.insert(self.parsechain, config) end +function Map.state_handler(self, state) + return state +end + -- Use optimized UCI writing -function Map.parse(self) - Node.parse(self) +function Map.parse(self, readinput, ...) + self.readinput = (readinput ~= false) + self:_run_hooks("on_parse") + + 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 luci.http.formvalue("cbi.apply") then + if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then + self:_run_hooks("on_before_commit") for i, config in ipairs(self.parsechain) do self.uci:commit(config) -- Refresh data because commit changes section names self.uci:load(config) end + self:_run_hooks("on_commit", "on_after_commit", "on_before_apply") if self.apply_on_parse then self.uci:apply(self.parsechain) + self:_run_hooks("on_apply", "on_after_apply") else self._apply = function() local cmd = self.uci:apply(self.parsechain, true) @@ -343,17 +397,33 @@ function Map.parse(self) self.uci:unload(config) end if type(self.commit_handler) == "function" then - self:commit_handler() + self:commit_handler(self:submitstate()) + end + end + + if self:submitstate() then + 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 end + + return self:state_handler(self.state) end function Map.render(self, ...) + self:_run_hooks("on_init") Node.render(self, ...) if self._apply then local fp = self._apply() fp:read("*a") fp:close() + self:_run_hooks("on_apply") end end @@ -402,6 +472,187 @@ function Map.get(self, section, option) end end +--[[ +Compound - Container +]]-- +Compound = class(Node) + +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 + + 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 + + +--[[ +Delegator - Node controller +]]-- +Delegator = class(Node) +function Delegator.__init__(self, ...) + Node.__init__(self, ...) + self.nodes = {} + 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" +end + +function Delegator.set(self, name, node) + assert(not self.nodes[name], "Duplicate entry") + + self.nodes[name] = node +end + +function Delegator.add(self, name, node) + node = self:set(name, node) + self.defaultpath[#self.defaultpath+1] = name +end + +function Delegator.insert_after(self, name, after) + local n = #self.chain + 1 + for k, v in ipairs(self.chain) do + if v == after then + n = k + 1 + break + end + 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) + 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, ...) + if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then + if self:_run_hooks("on_cancel") then + return FORM_DONE + end + end + + if not Map.formvalue(self, "cbi.delg.current") then + self:_run_hooks("on_init") + end + + 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 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 Map.formvalue(self, "cbi.submit") then + return FORM_NODATA + elseif stat > FORM_PROCEED + and (not newcurrent or not self:get(newcurrent)) then + 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: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 + end +end + +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.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 --[[ Page - A simple node @@ -423,10 +674,25 @@ function SimpleForm.__init__(self, config, title, description, data) self.data = data or {} self.template = "cbi/simpleform" self.dorender = true + self.pageaction = false + self.readinput = true end -function SimpleForm.parse(self, ...) - if luci.http.formvalue("cbi.submit") then +SimpleForm.formvalue = Map.formvalue +SimpleForm.formvaluetable = Map.formvaluetable + +function SimpleForm.parse(self, readinput, ...) + self.readinput = (readinput ~= false) + + if self:formvalue("cbi.skip") then + 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 @@ -436,15 +702,22 @@ function SimpleForm.parse(self, ...) 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 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 + 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 function SimpleForm.render(self, ...) @@ -453,6 +726,10 @@ function SimpleForm.render(self, ...) end end +function SimpleForm.submitstate(self) + return self:formvalue("cbi.submit") +end + function SimpleForm.section(self, class, ...) if instanceof(class, AbstractSection) then local obj = class(self, ...) @@ -506,6 +783,13 @@ function SimpleForm.get_scheme() end +Form = class(SimpleForm) + +function Form.__init__(self, ...) + SimpleForm.__init__(self, ...) + self.embedded = true +end + --[[ AbstractSection @@ -523,12 +807,26 @@ function AbstractSection.__init__(self, map, sectiontype, ...) self.tag_error = {} self.tag_invalid = {} self.tag_deperror = {} + self.changed = false self.optional = true self.addremove = false 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 @@ -547,9 +845,6 @@ function AbstractSection.option(self, class, option, ...) 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 @@ -560,6 +855,31 @@ function AbstractSection.option(self, class, option, ...) 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 @@ -568,11 +888,12 @@ function AbstractSection.parse_optionals(self, section) self.optionals[section] = {} - local field = luci.http.formvalue("cbi.opt."..self.config.."."..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 field == v.option then field = nil + self.map.proceed = true else table.insert(self.optionals[section], v) end @@ -597,7 +918,7 @@ function AbstractSection.parse_dynamic(self, section) end local arr = luci.util.clone(self:cfgvalue(section)) - local form = luci.http.formvaluetable("cbid."..self.config.."."..section) + local form = self.map:formvaluetable("cbid."..self.config.."."..section) for k, v in pairs(form) do arr[k] = v end @@ -612,6 +933,7 @@ function AbstractSection.parse_dynamic(self, section) end if create and key:sub(1, 1) ~= "." then + self.map.proceed = true self:add_dynamic(key, true) end end @@ -622,8 +944,15 @@ function AbstractSection.cfgvalue(self, section) return self.map:get(section) end +-- Push events +function AbstractSection.push_events(self) + --luci.util.append(self.map.events, self.events) + self.map.changed = true +end + -- Removes the section function AbstractSection.remove(self, section) + self.map.proceed = true return self.map:del(section) end @@ -632,7 +961,7 @@ function AbstractSection.create(self, section) 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 @@ -650,6 +979,8 @@ function AbstractSection.create(self, section) end end + self.map.proceed = true + return stat end @@ -666,11 +997,20 @@ Table = class(AbstractSection) 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 function datasource.del(...) @@ -687,9 +1027,10 @@ function Table.__init__(self, form, data, ...) self.anonymous = true end -function Table.parse(self) +function Table.parse(self, readinput) + self.map.readinput = (readinput ~= false) 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 @@ -705,6 +1046,10 @@ function Table.cfgsections(self) return sections end +function Table.update(self, data) + self.data = data +end + --[[ @@ -714,7 +1059,6 @@ 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 @@ -739,11 +1083,12 @@ function NamedSection.parse(self, novld) if self.addremove then local path = self.config.."."..s if active then -- Remove the section - if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then + if self.map:formvalue("cbi.rns."..path) and self:remove(s) then + self:push_events() return end else -- Create and apply default values - if luci.http.formvalue("cbi.cns."..path) then + if self.map:formvalue("cbi.cns."..path) then self:create(s) return end @@ -752,7 +1097,7 @@ function NamedSection.parse(self, novld) 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 novld and not self.override_scheme and self.map.scheme then @@ -760,6 +1105,10 @@ function NamedSection.parse(self, novld) end end AbstractSection.parse_optionals(self, s) + + if self.changed then + self:push_events() + end end end @@ -774,7 +1123,6 @@ 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.deps = {} @@ -813,7 +1161,7 @@ function TypedSection.parse(self, novld) if self.addremove then -- Remove local crval = REMOVE_PREFIX .. self.config - local name = luci.http.formvaluetable(crval) + local name = self.map:formvaluetable(crval) for k,v in pairs(name) do if k:sub(-2) == ".x" then k = k:sub(1, #k - 2) @@ -827,8 +1175,8 @@ function TypedSection.parse(self, novld) 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 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) @@ -841,7 +1189,7 @@ function TypedSection.parse(self, novld) -- Create local created local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype - local name = luci.http.formvalue(crval) + local name = self.map:formvalue(crval) if self.anonymous then if name then created = self:create() @@ -872,6 +1220,10 @@ function TypedSection.parse(self, novld) AbstractSection.parse_optionals(self, created) end end + + if created or self.changed then + self:push_events() + end end -- Verifies scope of sections @@ -929,10 +1281,11 @@ function AbstractValue.__init__(self, map, section, option, ...) self.tag_reqerror = {} self.tag_error = {} self.deps = {} + self.subdeps = {} --self.cast = "string" self.track_missing = false - --self.rmempty = false + self.rmempty = true self.default = nil self.size = nil self.optional = false @@ -943,9 +1296,6 @@ function 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.rmempty == nil then - self.rmempty = not vs.required - end if self.cast == nil then self.cast = (vs.type == "list") and "list" or "string" end @@ -989,12 +1339,12 @@ end -- Return whether this object should be created function AbstractValue.formcreated(self, section) local key = "cbi.opt."..self.config.."."..section - return (luci.http.formvalue(key) == self.option) + return (self.map:formvalue(key) == self.option) end -- Returns the formvalue for this object function AbstractValue.formvalue(self, section) - return luci.http.formvalue(self:cbid(section)) + return self.map:formvalue(self:cbid(section)) end function AbstractValue.additional(self, value) @@ -1005,23 +1355,63 @@ function AbstractValue.mandatory(self, value) 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 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 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 - self:write(section, fvalue) + if self:write(section, fvalue) then + -- Push events + self.section.changed = true + --luci.util.append(self.map.events, self.events) + end end else -- Unset the UCI or error if self.rmempty or self.optional then - self:remove(section) - elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then - self.tag_missing[section] = true + if self:remove(section) then + -- Push events + self.section.changed = true + --luci.util.append(self.map.events, self.events) + end + 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 @@ -1033,6 +1423,7 @@ function AbstractValue.render(self, s, scope) 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 @@ -1221,7 +1612,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 @@ -1319,7 +1710,7 @@ function StaticList.validate(self, value) 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 @@ -1343,6 +1734,11 @@ function DynamicList.value(self, key, val) 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} @@ -1350,8 +1746,8 @@ function DynamicList.formvalue(self, section) local valid = {} for i, v in ipairs(value) do if v and #v > 0 - and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) - and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then + and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i) + and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then table.insert(valid, v) end end @@ -1396,9 +1792,15 @@ function FileUpload.__init__(self, ...) end end +function FileUpload.formcreated(self, section) + return AbstractValue.formcreated(self, section) or + self.map:formvalue("cbi.rlf."..section.."."..self.option) or + self.map: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 + if val and fs.access(val) then return val end return nil @@ -1407,12 +1809,12 @@ 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") + if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and + not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x") then return val end - luci.fs.unlink(val) + fs.unlink(val) self.value = nil end return nil @@ -1420,6 +1822,14 @@ 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 + if val and fs.access(val) then 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