Backport Skip-Button support
[project/luci.git] / libs / cbi / luasrc / cbi.lua
index b37e213..ebca729 100644 (file)
@@ -27,21 +27,22 @@ 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 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_INVALID = -1
 FORM_CHANGED =  2
+FORM_SKIP    =  4
 
 AUTO = true
 
@@ -57,10 +58,10 @@ function load(cbimap, ...)
 
        local upldir = "/lib/uci/upload/"
        local cbidir = luci.util.libpath() .. "/model/cbi/"
-       
-       assert(luci.fs.stat(cbimap) or luci.fs.stat(cbidir..cbimap..".lua"), 
+
+       assert(luci.fs.stat(cbimap) or luci.fs.stat(cbidir..cbimap..".lua"),
         "Model not found!")
-         
+
        local func, err = loadfile(cbimap)
        if not func then
                func, err = loadfile(cbidir..cbimap..".lua")
@@ -166,7 +167,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
@@ -295,12 +296,13 @@ function Map.__init__(self, config, ...)
        self.template = "cbi/map"
        self.apply_on_parse = nil
        self.readinput = true
+       self.proceed = false
 
        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
@@ -343,13 +345,19 @@ end
 -- 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)
 
@@ -378,10 +386,12 @@ function Map.parse(self, readinput, ...)
        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
@@ -456,12 +466,12 @@ end
 
 function Compound.parse(self, ...)
        local cstate, state = 0, 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
 
@@ -479,13 +489,13 @@ 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")
        assert(not self.nodes[name], "Duplicate entry")
-       
+
        self.nodes[name] = state
        self:append(state)
-       
+
        return state
 end
 
@@ -508,10 +518,10 @@ end
 function Delegator.parse(self, ...)
        local active = self:getactive()
        assert(active, "Invalid state")
-       
+
        local cstate = active.node:parse()
        self.active = active.transistor(self, active.node, cstate)
-       
+
        if not self.active then
                return FORM_DONE
        else
@@ -525,8 +535,8 @@ function Delegator.render(self, ...)
 end
 
 function Delegator.getactive(self)
-       return self:get(Map.formvalue(self, "cbi.delegated") 
-               or (self.children[1] and self.children[1].name)) 
+       return self:get(Map.formvalue(self, "cbi.delegated")
+               or (self.children[1] and self.children[1].name))
 end
 
 --[[
@@ -558,6 +568,11 @@ 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:submitstate() then
                Node.parse(self, 1, ...)
        end
@@ -577,7 +592,12 @@ function SimpleForm.parse(self, readinput, ...)
                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
 
@@ -644,6 +664,13 @@ function SimpleForm.get_scheme()
 end
 
 
+Form = class(SimpleForm)
+
+function Form.__init__(self, ...)
+       SimpleForm.__init__(self, ...)
+       self.embedded = true
+end
+
 
 --[[
 AbstractSection
@@ -713,6 +740,7 @@ function AbstractSection.parse_optionals(self, section)
                        if field == v.option then
                                field = nil
                        else
+                               self.map.proceed = true
                                table.insert(self.optionals[section], v)
                        end
                end
@@ -751,6 +779,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
@@ -769,6 +798,7 @@ end
 
 -- Removes the section
 function AbstractSection.remove(self, section)
+       self.map.proceed = true
        return self.map:del(section)
 end
 
@@ -795,6 +825,8 @@ function AbstractSection.create(self, section)
                end
        end
 
+       self.map.proceed = true
+
        return stat
 end
 
@@ -811,17 +843,18 @@ 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
@@ -859,6 +892,10 @@ function Table.cfgsections(self)
        return sections
 end
 
+function Table.update(self, data)
+       self.data = data
+end
+
 
 
 --[[
@@ -915,7 +952,7 @@ function NamedSection.parse(self, novld)
                        end
                end
                AbstractSection.parse_optionals(self, s)
-               
+
                if self.changed then
                        self:push_events()
                end
@@ -987,7 +1024,7 @@ function TypedSection.parse(self, novld)
        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)
@@ -1095,7 +1132,7 @@ function AbstractValue.__init__(self, map, section, option, ...)
        --self.cast = "string"
 
        self.track_missing = false
-       --self.rmempty   = false
+       self.rmempty   = true
        self.default   = nil
        self.size      = nil
        self.optional  = false
@@ -1106,9 +1143,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
@@ -1168,13 +1202,13 @@ 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 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
+               if not fvalue and not novld then
                        if self.error then
                                self.error[section] = "invalid"
                        else
@@ -1186,7 +1220,7 @@ function AbstractValue.parse(self, section)
                        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
@@ -1196,7 +1230,7 @@ function AbstractValue.parse(self, section)
                                self.section.changed = true
                                --luci.util.append(self.map.events, self.events)
                        end
-               elseif cvalue ~= fvalue then
+               elseif cvalue ~= fvalue and not novld then
                        self:write(section, fvalue or "")
                        if self.error then
                                self.error[section] = "missing"
@@ -1501,7 +1535,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
@@ -1525,6 +1559,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}