Fix errorhandling in Delegators
[project/luci.git] / libs / cbi / luasrc / cbi.lua
index fe20773..db76e3b 100644 (file)
@@ -27,17 +27,24 @@ limitations under the License.
 module("luci.cbi", package.seeall)
 
 require("luci.template")
 module("luci.cbi", package.seeall)
 
 require("luci.template")
-require("luci.util")
+local util = require("luci.util")
 require("luci.http")
 require("luci.uvl")
 
 require("luci.http")
 require("luci.uvl")
 
+
+--local event      = require "luci.sys.event"
+local fs         = require("nixio.fs")
 local uci        = require("luci.model.uci")
 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_NODATA  =  0
+FORM_PROCEED =  0
 FORM_VALID   =  1
 FORM_VALID   =  1
+FORM_DONE       =  1
 FORM_INVALID = -1
 FORM_INVALID = -1
+FORM_CHANGED =  2
+FORM_SKIP    =  4
 
 AUTO = true
 
 
 AUTO = true
 
@@ -46,35 +53,102 @@ REMOVE_PREFIX = "cbi.rts."
 
 -- Loads a CBI map from given file, creating an environment and returns it
 function load(cbimap, ...)
 
 -- Loads a CBI map from given file, creating an environment and returns it
 function load(cbimap, ...)
-       require("luci.fs")
-       require("luci.i18n")
+       local fs   = require "nixio.fs"
+       local i18n = require "luci.i18n"
        require("luci.config")
        require("luci.util")
 
        require("luci.config")
        require("luci.util")
 
+       local upldir = "/lib/uci/upload/"
        local cbidir = luci.util.libpath() .. "/model/cbi/"
        local cbidir = luci.util.libpath() .. "/model/cbi/"
-       local func, err = loadfile(cbidir..cbimap..".lua")
 
 
+       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
        if not func then
-               return nil
+               func, err = loadfile(cbidir..cbimap..".lua") or
+                       loadfile(cbidir..cbimap..".lua.gz")
        end
        end
+       assert(func, err)
 
        luci.i18n.loadc("cbi")
 
        luci.i18n.loadc("cbi")
+       luci.i18n.loadc("uvl")
+
+       local env = {
+               translate=i18n.translate,
+               translatef=i18n.translatef,
+               arg={...}
+       }
 
 
-       luci.util.resfenv(func)
-       luci.util.updfenv(func, luci.cbi)
-       luci.util.extfenv(func, "translate", luci.i18n.translate)
-       luci.util.extfenv(func, "translatef", luci.i18n.translatef)
-       luci.util.extfenv(func, "arg", {...})
+       setfenv(func, setmetatable(env, {__index =
+               function(tbl, key)
+                       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
 
        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
 
                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
 
        return maps
 end
 
@@ -84,28 +158,43 @@ local function _uvl_validate_section(node, name)
        luci.uvl.STRICT_UNKNOWN_OPTIONS = false
        luci.uvl.STRICT_UNKNOWN_SECTIONS = false
 
        luci.uvl.STRICT_UNKNOWN_OPTIONS = false
        luci.uvl.STRICT_UNKNOWN_SECTIONS = false
 
-       local stat, err = node.map.validator:validate_section(node.config, name, co)
-       if err then
-               node.map.save = false
-               if err:is(luci.uvl.errors.ERR_DEPENDENCY) then
-                       node.tag_deperror[name] = true
-               else
-                       node.tag_invalid[name] = true
+       local function tag_fields(e)
+               if e.option and node.fields[e.option] then
+                       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
                end
-               for i, v in ipairs(err.childs) do
-                       if v.option and node.fields[v.option] then
-                               if v:is(luci.uvl.errors.ERR_DEPENDENCY) then
-                                       node.fields[v.option].tag_reqerror[name] = true
-                               elseif v:is(luci.uvl.errors.ERR_OPT_REQUIRED) then
-                                       node.fields[v.option].tag_missing[name] = true
-                                       node.tag_deperror[name] = true
-                               elseif v:is(luci.uvl.errors.ERR_OPTION) then
-                                       node.fields[v.option].tag_invalid[name] = true
-                               end
+       end
+
+       local function tag_section(e)
+               local s = { }
+               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
+                               table.insert( s, c:string() )
+                       end
+               end
+               if #s > 0 then
+                       if node.error then
+                               node.error[name] = s
+                       else
+                               node.error = { [name] = s }
                        end
                end
        end
 
                        end
                end
        end
 
+       local stat, err = node.map.validator:validate_section(node.config, name, co)
+       if err then
+               node.map.save = false
+               tag_fields(err)
+               tag_section(err)
+       end
+
 end
 
 local function _uvl_strip_remote_dependencies(deps)
 end
 
 local function _uvl_strip_remote_dependencies(deps)
@@ -148,6 +237,13 @@ function Node._i18n(self, config, section, option, title, description)
        end
 end
 
        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)
 -- Append child nodes
 function Node.append(self, obj)
        table.insert(self.children, obj)
@@ -190,6 +286,11 @@ function Template.render(self)
        luci.template.render(self.template, {self=self})
 end
 
        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
 
 --[[
 Map - A map describing a configuration file
@@ -203,15 +304,31 @@ function Map.__init__(self, config, ...)
        self.config = config
        self.parsechain = {self.config}
        self.template = "cbi/map"
        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.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)
        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)
 end
 
 function Map.get_scheme(self, sectiontype, option)
@@ -223,36 +340,83 @@ function Map.get_scheme(self, sectiontype, option)
        end
 end
 
        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
 
 
 -- 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
 -- Use optimized UCI writing
-function Map.parse(self, ...)
+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
        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
                        for i, config in ipairs(self.parsechain) do
                                self.uci:commit(config)
                        for i, config in ipairs(self.parsechain) do
                                self.uci:commit(config)
-                               self.uci:apply(config)
-       
+
                                -- Refresh data because commit changes section names
                                self.uci:load(config)
                        end
                                -- Refresh data because commit changes section names
                                self.uci:load(config)
                        end
-       
+                       if self.apply_on_parse then
+                               self.uci:apply(self.parsechain)
+                       else
+                               self._apply = function()
+                                       local cmd = self.uci:apply(self.parsechain, true)
+                                       return io.popen(cmd)
+                               end
+                       end
+
                        -- Reparse sections
                        -- Reparse sections
-                       Node.parse(self, ...)
-       
+                       Node.parse(self, true)
+
                end
                for i, config in ipairs(self.parsechain) do
                        self.uci:unload(config)
                end
                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 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, ...)
+       Node.render(self, ...)
+       if self._apply then
+               local fp = self._apply()
+               fp:read("*a")
+               fp:close()
        end
 end
 
        end
 end
 
@@ -301,6 +465,164 @@ function Map.get(self, section, option)
        end
 end
 
        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_back = false
+       self.allow_finish = false
+       self.template = "cbi/delegator"
+end
+
+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] = 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
+       for k, v in ipairs(self.chain) do
+               if v == state 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)
+       return self.nodes[name]
+end
+
+function Delegator.parse(self, ...)
+       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 not newcurrent or not self:get(newcurrent) then
+               return FORM_DONE
+       else
+               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.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
 
 --[[
 Page - A simple node
@@ -322,10 +644,21 @@ function SimpleForm.__init__(self, config, title, description, data)
        self.data = data or {}
        self.template = "cbi/simpleform"
        self.dorender = true
        self.data = data or {}
        self.template = "cbi/simpleform"
        self.dorender = true
+       self.pageaction = false
+       self.readinput = true
 end
 
 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:submitstate() then
                Node.parse(self, 1, ...)
        end
 
                Node.parse(self, 1, ...)
        end
 
@@ -335,15 +668,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])
                        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 =
                end
        end
 
        local state =
-               not luci.http.formvalue("cbi.submit") and 0
-               or valid and 1
-               or -1
-
-       self.dorender = not self.handle or self:handle(state, self.data) ~= false
+               not self:submitstate() and FORM_NODATA
+               or valid and FORM_VALID
+               or FORM_INVALID
+
+       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, ...)
 end
 
 function SimpleForm.render(self, ...)
@@ -352,6 +692,10 @@ function SimpleForm.render(self, ...)
        end
 end
 
        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, ...)
 function SimpleForm.section(self, class, ...)
        if instanceof(class, AbstractSection) then
                local obj  = class(self, ...)
@@ -405,6 +749,13 @@ function SimpleForm.get_scheme()
 end
 
 
 end
 
 
+Form = class(SimpleForm)
+
+function Form.__init__(self, ...)
+       SimpleForm.__init__(self, ...)
+       self.embedded = true
+end
+
 
 --[[
 AbstractSection
 
 --[[
 AbstractSection
@@ -422,6 +773,7 @@ function AbstractSection.__init__(self, map, sectiontype, ...)
        self.tag_error = {}
        self.tag_invalid = {}
        self.tag_deperror = {}
        self.tag_error = {}
        self.tag_invalid = {}
        self.tag_deperror = {}
+       self.changed = false
 
        self.optional = true
        self.addremove = false
 
        self.optional = true
        self.addremove = false
@@ -467,11 +819,12 @@ function AbstractSection.parse_optionals(self, section)
 
        self.optionals[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
        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
                        else
                                table.insert(self.optionals[section], v)
                        end
@@ -496,7 +849,7 @@ function AbstractSection.parse_dynamic(self, section)
        end
 
        local arr  = luci.util.clone(self:cfgvalue(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
        for k, v in pairs(form) do
                arr[k] = v
        end
@@ -511,6 +864,7 @@ function AbstractSection.parse_dynamic(self, section)
                end
 
                if create and key:sub(1, 1) ~= "." then
                end
 
                if create and key:sub(1, 1) ~= "." then
+                       self.map.proceed = true
                        self:add_dynamic(key, true)
                end
        end
                        self:add_dynamic(key, true)
                end
        end
@@ -521,8 +875,15 @@ function AbstractSection.cfgvalue(self, section)
        return self.map:get(section)
 end
 
        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)
 -- Removes the section
 function AbstractSection.remove(self, section)
+       self.map.proceed = true
        return self.map:del(section)
 end
 
        return self.map:del(section)
 end
 
@@ -531,7 +892,7 @@ function AbstractSection.create(self, section)
        local stat
 
        if section then
        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
        else
                section = self.map:add(self.sectiontype)
                stat = section
@@ -549,6 +910,8 @@ function AbstractSection.create(self, section)
                end
        end
 
                end
        end
 
+       self.map.proceed = true
+
        return stat
 end
 
        return stat
 end
 
@@ -565,11 +928,20 @@ Table = class(AbstractSection)
 
 function Table.__init__(self, form, data, ...)
        local datasource = {}
 
 function Table.__init__(self, form, data, ...)
        local datasource = {}
+       local tself = self
        datasource.config = "table"
        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)
 
        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(...)
        end
 
        function datasource.del(...)
@@ -586,9 +958,10 @@ function Table.__init__(self, form, data, ...)
        self.anonymous = true
 end
 
        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
        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
                        Node.parse(self, k)
                end
        end
@@ -604,6 +977,10 @@ function Table.cfgsections(self)
        return sections
 end
 
        return sections
 end
 
+function Table.update(self, data)
+       self.data = data
+end
+
 
 
 --[[
 
 
 --[[
@@ -614,7 +991,7 @@ NamedSection = class(AbstractSection)
 function NamedSection.__init__(self, map, section, stype, ...)
        AbstractSection.__init__(self, map, stype, ...)
        Node._i18n(self, map.config, section, nil, ...)
 function NamedSection.__init__(self, map, section, stype, ...)
        AbstractSection.__init__(self, map, stype, ...)
        Node._i18n(self, map.config, section, nil, ...)
-       
+
        -- Defaults
        self.addremove = false
 
        -- Defaults
        self.addremove = false
 
@@ -631,18 +1008,19 @@ function NamedSection.__init__(self, map, section, stype, ...)
        self.section = section
 end
 
        self.section = section
 end
 
-function NamedSection.parse(self)
+function NamedSection.parse(self, novld)
        local s = self.section
        local active = self:cfgvalue(s)
 
        if self.addremove then
                local path = self.config.."."..s
                if active then -- Remove the section
        local s = self.section
        local active = self:cfgvalue(s)
 
        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
                                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
                                self:create(s)
                                return
                        end
@@ -651,14 +1029,18 @@ function NamedSection.parse(self)
 
        if active then
                AbstractSection.parse_dynamic(self, 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)
                        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
                AbstractSection.parse_optionals(self, s)
                                _uvl_validate_section(self, s)
                        end
                end
                AbstractSection.parse_optionals(self, s)
+
+               if self.changed then
+                       self:push_events()
+               end
        end
 end
 
        end
 end
 
@@ -708,12 +1090,15 @@ function TypedSection.depends(self, option, value)
        table.insert(self.deps, {option=option, value=value})
 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
        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
                for k,v in pairs(name) do
+                       if k:sub(-2) == ".x" then
+                               k = k:sub(1, #k - 2)
+                       end
                        if self:cfgvalue(k) and self:checkscope(k) then
                                self:remove(k)
                        end
                        if self:cfgvalue(k) and self:checkscope(k) then
                                self:remove(k)
                        end
@@ -723,10 +1108,10 @@ function TypedSection.parse(self)
        local co
        for i, k in ipairs(self:cfgsections()) do
                AbstractSection.parse_dynamic(self, k)
        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 not self.override_scheme and self.map.scheme then
+               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)
                        end
                end
                                _uvl_validate_section(self, k)
                        end
                end
@@ -735,11 +1120,12 @@ function TypedSection.parse(self)
 
        if self.addremove then
                -- Create
 
        if self.addremove then
                -- Create
+               local created
                local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
                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
                if self.anonymous then
                        if name then
-                               self:create()
+                               created = self:create()
                        end
                else
                        if name then
                        end
                else
                        if name then
@@ -755,10 +1141,21 @@ function TypedSection.parse(self)
                                end
 
                                if name and #name > 0 then
                                end
 
                                if name and #name > 0 then
-                                       self:create(name)
+                                       created = self:create(name) and name
+                                       if not created then
+                                               self.invalid_cts = true
+                                       end
                                end
                        end
                end
                                end
                        end
                end
+
+               if created then
+                       AbstractSection.parse_optionals(self, created)
+               end
+       end
+
+       if created or self.changed then
+               self:push_events()
        end
 end
 
        end
 end
 
@@ -817,22 +1214,28 @@ function AbstractValue.__init__(self, map, section, option, ...)
        self.tag_reqerror = {}
        self.tag_error = {}
        self.deps = {}
        self.tag_reqerror = {}
        self.tag_error = {}
        self.deps = {}
-       self.cast = "string"
+       --self.cast = "string"
 
        self.track_missing = false
 
        self.track_missing = false
-       self.rmempty   = false
+       self.rmempty   = true
        self.default   = nil
        self.size      = nil
        self.optional  = 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)
        -- 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.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.title       = self.title or vs.title
                self.description = self.description or vs.descr
+               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
 
                if vs.depends and not self.override_dependencies then
                        for i, deps in ipairs(vs.depends) do
@@ -843,6 +1246,8 @@ function AbstractValue.__init__(self, map, section, option, ...)
                        end
                end
        end
                        end
                end
        end
+
+       self.cast = self.cast or "string"
 end
 
 -- Add a dependencie to another section field
 end
 
 -- Add a dependencie to another section field
@@ -866,12 +1271,12 @@ end
 -- Return whether this object should be created
 function AbstractValue.formcreated(self, section)
        local key = "cbi.opt."..self.config.."."..section
 -- 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)
 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)
 end
 
 function AbstractValue.additional(self, value)
@@ -882,23 +1287,47 @@ function AbstractValue.mandatory(self, value)
        self.rmempty = not value
 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)
 
        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))
                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
                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
                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
                end
        end
 end
@@ -936,14 +1365,16 @@ end
 -- Return the UCI value of this object
 function AbstractValue.cfgvalue(self, section)
        local value = self.map:get(section, self.option)
 -- 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
        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
 
        end
 end
 
@@ -997,6 +1428,20 @@ function DummyValue.__init__(self, ...)
        self.value = nil
 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
 function DummyValue.parse(self)
 
 end
@@ -1050,23 +1495,23 @@ function ListValue.__init__(self, ...)
        self.vallist = {}
        self.size   = 1
        self.widget = "select"
        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 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
                                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
                                                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
                        end
                end
        end
@@ -1180,7 +1625,7 @@ function StaticList.validate(self, value)
 
        local valid = {}
        for i, v in ipairs(value) do
 
        local valid = {}
        for i, v in ipairs(value) do
-               if luci.util.contains(self.valuelist, v) then
+               if luci.util.contains(self.keylist, v) then
                        table.insert(valid, v)
                end
        end
                        table.insert(valid, v)
                end
        end
@@ -1204,13 +1649,20 @@ function DynamicList.value(self, key, val)
        table.insert(self.vallist, tostring(val))
 end
 
        table.insert(self.vallist, tostring(val))
 end
 
-function DynamicList.validate(self, value, section)
+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}
 
        local valid = {}
        for i, v in ipairs(value) do
        value = (type(value) == "table") and value or {value}
 
        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) then
+               if v and #v > 0
+                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
                        table.insert(valid, v)
                end
        end
@@ -1241,3 +1693,58 @@ function Button.__init__(self, ...)
        self.inputstyle = nil
        self.rmempty = true
 end
        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
+               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 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 self.map:formvalue("cbi.rlf."..section.."."..self.option) and
+                  not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
+               then
+                       return val
+               end
+               fs.unlink(val)
+               self.value = nil
+       end
+       return nil
+end
+
+function FileUpload.remove(self, section)
+       local val = AbstractValue.formvalue(self, section)
+       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