libs/cbi: Fix option initialisation errors
[project/luci.git] / libs / cbi / luasrc / cbi.lua
index 9a94aba..6432a25 100644 (file)
@@ -29,10 +29,9 @@ module("luci.cbi", package.seeall)
 require("luci.template")
 require("luci.util")
 require("luci.http")
 require("luci.template")
 require("luci.util")
 require("luci.http")
-require("luci.model.uci")
 require("luci.uvl")
 
 require("luci.uvl")
 
-local uci        = luci.model.uci
+local uci        = require("luci.model.uci")
 local class      = luci.util.class
 local instanceof = luci.util.instanceof
 
 local class      = luci.util.class
 local instanceof = luci.util.instanceof
 
@@ -40,30 +39,35 @@ FORM_NODATA  =  0
 FORM_VALID   =  1
 FORM_INVALID = -1
 
 FORM_VALID   =  1
 FORM_INVALID = -1
 
+AUTO = true
+
 CREATE_PREFIX = "cbi.cts."
 REMOVE_PREFIX = "cbi.rts."
 
 -- Loads a CBI map from given file, creating an environment and returns it
 function load(cbimap, ...)
        require("luci.fs")
 CREATE_PREFIX = "cbi.cts."
 REMOVE_PREFIX = "cbi.rts."
 
 -- Loads a CBI map from given file, creating an environment and returns it
 function load(cbimap, ...)
        require("luci.fs")
-       require("luci.i18n")
+       local i18n = require "luci.i18n"
        require("luci.config")
        require("luci.util")
 
        local cbidir = luci.util.libpath() .. "/model/cbi/"
        require("luci.config")
        require("luci.util")
 
        local cbidir = luci.util.libpath() .. "/model/cbi/"
-       local func, err = loadfile(cbidir..cbimap..".lua")
-
-       if not func then
-               return nil
-       end
+       local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
+       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()}
 
@@ -71,23 +75,69 @@ function load(cbimap, ...)
                if not instanceof(map, Node) then
                        error("CBI map returns no valid map object!")
                        return nil
                if not instanceof(map, Node) then
                        error("CBI map returns no valid map object!")
                        return nil
+               else
+                       map:prepare()
                end
        end
 
        return maps
 end
 
                end
        end
 
        return maps
 end
 
+local function _uvl_validate_section(node, name)
+       local co = node.map:get()
+
+       luci.uvl.STRICT_UNKNOWN_OPTIONS = false
+       luci.uvl.STRICT_UNKNOWN_SECTIONS = false
+
+       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
+
+       local function tag_section(e)
+               local s = { }
+               for _, c in ipairs(e.childs) 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
 
 
-function _uvl_strip_remote_dependencies(deps)
+       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)
        local clean = {}
        local clean = {}
-       
+
        for k, v in pairs(deps) do
                k = k:gsub("%$config%.%$section%.", "")
        for k, v in pairs(deps) do
                k = k:gsub("%$config%.%$section%.", "")
-               if k:match("^[%w_]+$") then
+               if k:match("^[%w_]+$") and type(v) == "string" then
                        clean[k] = v
                end
        end
                        clean[k] = v
                end
        end
-       
+
        return clean
 end
 
        return clean
 end
 
@@ -118,6 +168,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)
@@ -173,12 +230,16 @@ 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"
-       if not uci.load_config(self.config) then
+       self.apply_on_parse = nil
+       self.uci = uci.cursor()
+       self.save = true
+       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)
                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.get_scheme(self, sectiontype, option)
 end
 
 function Map.get_scheme(self, sectiontype, option)
@@ -190,15 +251,6 @@ function Map.get_scheme(self, sectiontype, option)
        end
 end
 
        end
 end
 
-function Map.render(self, ...)
-       if self.stateful then
-               uci.load_state(self.config)
-       else
-               uci.load_config(self.config)
-       end
-       Node.render(self, ...)
-end
-
 
 -- Chain foreign config
 function Map.chain(self, config)
 
 -- Chain foreign config
 function Map.chain(self, config)
@@ -206,35 +258,45 @@ function Map.chain(self, config)
 end
 
 -- Use optimized UCI writing
 end
 
 -- Use optimized UCI writing
-function Map.parse(self, ...)
-       if self.stateful then
-               uci.load_state(self.config)
-       else
-               uci.load_config(self.config)
-       end
-       
-       Node.parse(self, ...)
-       
-       for i, config in ipairs(self.parsechain) do
-               uci.save_config(config)
-       end
-       if luci.http.formvalue("cbi.apply") then
-               for i, config in ipairs(self.parsechain) do
-                       uci.commit(config)
-                       if luci.config.uci_oncommit and luci.config.uci_oncommit[config] then
-                               luci.util.exec(luci.config.uci_oncommit[config])
-                       end
+function Map.parse(self)
+       Node.parse(self)
 
 
-                       -- Refresh data because commit changes section names
-                       uci.load_config(config)
+       if self.save then
+               for i, config in ipairs(self.parsechain) do
+                       self.uci:save(config)
                end
                end
+               if luci.http.formvalue("cbi.apply") then
+                       for i, config in ipairs(self.parsechain) do
+                               self.uci:commit(config)
+
+                               -- 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
-               Node.parse(self, ...)
+                       -- Reparse sections
+                       Node.parse(self, true)
 
 
+               end
+               for i, config in ipairs(self.parsechain) do
+                       self.uci:unload(config)
+               end
        end
        end
-       for i, config in ipairs(self.parsechain) do
-               uci.unload(config)
+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
 
@@ -251,35 +313,35 @@ end
 
 -- UCI add
 function Map.add(self, sectiontype)
 
 -- UCI add
 function Map.add(self, sectiontype)
-       return uci.add(self.config, sectiontype)
+       return self.uci:add(self.config, sectiontype)
 end
 
 -- UCI set
 function Map.set(self, section, option, value)
        if option then
 end
 
 -- UCI set
 function Map.set(self, section, option, value)
        if option then
-               return uci.set(self.config, section, option, value)
+               return self.uci:set(self.config, section, option, value)
        else
        else
-               return uci.set(self.config, section, value)
+               return self.uci:set(self.config, section, value)
        end
 end
 
 -- UCI del
 function Map.del(self, section, option)
        if option then
        end
 end
 
 -- UCI del
 function Map.del(self, section, option)
        if option then
-               return uci.delete(self.config, section, option)
+               return self.uci:delete(self.config, section, option)
        else
        else
-               return uci.delete(self.config, section)
+               return self.uci:delete(self.config, section)
        end
 end
 
 -- UCI get
 function Map.get(self, section, option)
        if not section then
        end
 end
 
 -- UCI get
 function Map.get(self, section, option)
        if not section then
-               return uci.get_all(self.config)
+               return self.uci:get_all(self.config)
        elseif option then
        elseif option then
-               return uci.get(self.config, section, option)
+               return self.uci:get(self.config, section, option)
        else
        else
-               return uci.get_all(self.config, section)
+               return self.uci:get_all(self.config, section)
        end
 end
 
        end
 end
 
@@ -310,17 +372,17 @@ function SimpleForm.parse(self, ...)
        if luci.http.formvalue("cbi.submit") then
                Node.parse(self, 1, ...)
        end
        if luci.http.formvalue("cbi.submit") then
                Node.parse(self, 1, ...)
        end
-               
+
        local valid = true
        local valid = true
-       for k, j in ipairs(self.children) do 
+       for k, j in ipairs(self.children) do
                for i, v in ipairs(j.children) do
                for i, v in ipairs(j.children) do
-                       valid = valid 
+                       valid = valid
                         and (not v.tag_missing or not v.tag_missing[1])
                         and (not v.tag_invalid or not v.tag_invalid[1])
                end
        end
                         and (not v.tag_missing or not v.tag_missing[1])
                         and (not v.tag_invalid or not v.tag_invalid[1])
                end
        end
-       
-       local state = 
+
+       local state =
                not luci.http.formvalue("cbi.submit") and 0
                or valid and 1
                or -1
                not luci.http.formvalue("cbi.submit") and 0
                or valid and 1
                or -1
@@ -356,9 +418,9 @@ function SimpleForm.field(self, class, ...)
        if not section then
                section = self:section(SimpleSection)
        end
        if not section then
                section = self:section(SimpleSection)
        end
-       
+
        if instanceof(class, AbstractValue) then
        if instanceof(class, AbstractValue) then
-               local obj  = class(self, ...)
+               local obj  = class(self, section, ...)
                obj.track_missing = true
                section:append(obj)
                return obj
                obj.track_missing = true
                section:append(obj)
                return obj
@@ -382,6 +444,11 @@ function SimpleForm.get(self, section, option)
 end
 
 
 end
 
 
+function SimpleForm.get_scheme()
+       return nil
+end
+
+
 
 --[[
 AbstractSection
 
 --[[
 AbstractSection
@@ -395,6 +462,10 @@ function AbstractSection.__init__(self, map, sectiontype, ...)
        self.config = map.config
        self.optionals = {}
        self.defaults = {}
        self.config = map.config
        self.optionals = {}
        self.defaults = {}
+       self.fields = {}
+       self.tag_error = {}
+       self.tag_invalid = {}
+       self.tag_deperror = {}
 
        self.optional = true
        self.addremove = false
 
        self.optional = true
        self.addremove = false
@@ -403,29 +474,29 @@ end
 
 -- Appends a new option
 function AbstractSection.option(self, class, option, ...)
 
 -- Appends a new option
 function AbstractSection.option(self, class, option, ...)
-       -- Autodetect form UVL
-       if not class or type(class) == "boolean"
-        and self.map:get_scheme(self.sectiontype, option) then
+       -- Autodetect from UVL
+       if class == true and self.map:get_scheme(self.sectiontype, option) then
                local vs = self.map:get_scheme(self.sectiontype, option)
                if vs.type == "boolean" then
                local vs = self.map:get_scheme(self.sectiontype, option)
                if vs.type == "boolean" then
-                       class = "Flag"
+                       class = Flag
                elseif vs.type == "list" then
                elseif vs.type == "list" then
-                       class = "DynamicList"
+                       class = DynamicList
                elseif vs.type == "enum" or vs.type == "reference" then
                elseif vs.type == "enum" or vs.type == "reference" then
-                       class = "ListValue"
+                       class = ListValue
                else
                else
-                       class = "Value"
+                       class = Value
                end
        end
                end
        end
-       
+
        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)
        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
                return obj
-       elseif not class or type(class) == "boolean" then
+       elseif class == true then
                error("No valid class was given and autodetection failed.")
        else
                error("class must be a descendant of AbstractValue")
                error("No valid class was given and autodetection failed.")
        else
                error("class must be a descendant of AbstractValue")
@@ -502,9 +573,9 @@ end
 -- Creates the section
 function AbstractSection.create(self, section)
        local stat
 -- Creates the section
 function AbstractSection.create(self, section)
        local stat
-       
+
        if section then
        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
@@ -540,15 +611,19 @@ function Table.__init__(self, form, data, ...)
        local datasource = {}
        datasource.config = "table"
        self.data = data
        local datasource = {}
        datasource.config = "table"
        self.data = data
-       
+
        function datasource.get(self, section, option)
                return data[section] and data[section][option]
        end
        function datasource.get(self, section, option)
                return data[section] and data[section][option]
        end
-       
+
        function datasource.del(...)
                return true
        end
        function datasource.del(...)
                return true
        end
-       
+
+       function datasource.get_scheme()
+               return nil
+       end
+
        AbstractSection.__init__(self, datasource, "table", ...)
        self.template = "cbi/tblsection"
        self.rowcolors = true
        AbstractSection.__init__(self, datasource, "table", ...)
        self.template = "cbi/tblsection"
        self.rowcolors = true
@@ -565,11 +640,11 @@ end
 
 function Table.cfgsections(self)
        local sections = {}
 
 function Table.cfgsections(self)
        local sections = {}
-       
+
        for i, v in luci.util.kspairs(self.data) do
                table.insert(sections, i)
        end
        for i, v in luci.util.kspairs(self.data) do
                table.insert(sections, i)
        end
-       
+
        return sections
 end
 
        return sections
 end
 
@@ -600,11 +675,10 @@ 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)
 
        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 self.addremove then
                local path = self.config.."."..s
                if active then -- Remove the section
@@ -623,6 +697,10 @@ function NamedSection.parse(self)
                AbstractSection.parse_dynamic(self, s)
                if luci.http.formvalue("cbi.submit") then
                        Node.parse(self, s)
                AbstractSection.parse_dynamic(self, s)
                if luci.http.formvalue("cbi.submit") then
                        Node.parse(self, s)
+
+                       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)
        end
                end
                AbstractSection.parse_optionals(self, s)
        end
@@ -659,7 +737,7 @@ end
 -- Return all matching UCI sections for this TypedSection
 function TypedSection.cfgsections(self)
        local sections = {}
 -- Return all matching UCI sections for this TypedSection
 function TypedSection.cfgsections(self)
        local sections = {}
-       uci.foreach(self.map.config, self.sectiontype,
+       self.map.uci:foreach(self.map.config, self.sectiontype,
                function (section)
                        if self:checkscope(section[".name"]) then
                                table.insert(sections, section[".name"])
                function (section)
                        if self:checkscope(section[".name"]) then
                                table.insert(sections, section[".name"])
@@ -674,14 +752,42 @@ 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
+               local name = luci.http.formvaluetable(crval)
+               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
+               end
+       end
+
+       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 novld and not self.override_scheme and self.map.scheme then
+                               _uvl_validate_section(self, k)
+                       end
+               end
+               AbstractSection.parse_optionals(self, k)
+       end
+
        if self.addremove then
                -- Create
        if self.addremove then
                -- Create
+               local created
                local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
                local name  = luci.http.formvalue(crval)
                if self.anonymous then
                        if name then
                local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
                local name  = luci.http.formvalue(crval)
                if self.anonymous then
                        if name then
-                               self:create()
+                               created = self:create()
                        end
                else
                        if name then
                        end
                else
                        if name then
@@ -696,32 +802,19 @@ function TypedSection.parse(self)
                                        self.err_invalid = true
                                end
 
                                        self.err_invalid = true
                                end
 
-                               if name and name:len() > 0 then
-                                       self:create(name)
+                               if name and #name > 0 then
+                                       created = self:create(name) and name
+                                       if not created then
+                                               self.invalid_cts = true
+                                       end
                                end
                        end
                end
 
                                end
                        end
                end
 
-               -- Remove
-               crval = REMOVE_PREFIX .. self.config
-               name = luci.http.formvaluetable(crval)
-               for k,v in pairs(name) do
-                       luci.util.perror(k)
-                       luci.util.perror(self:cfgvalue(k))
-                       luci.util.perror(self:checkscope(k))
-                       if self:cfgvalue(k) and self:checkscope(k) then
-                               self:remove(k)
-                       end
+               if created then
+                       AbstractSection.parse_optionals(self, created)
                end
        end
                end
        end
-
-       for i, k in ipairs(self:cfgsections()) do
-               AbstractSection.parse_dynamic(self, k)
-               if luci.http.formvalue("cbi.submit") then
-                       Node.parse(self, k)
-               end
-               AbstractSection.parse_optionals(self, k)
-       end
 end
 
 -- Verifies scope of sections
 end
 
 -- Verifies scope of sections
@@ -776,25 +869,35 @@ function AbstractValue.__init__(self, map, section, option, ...)
        self.config  = map.config
        self.tag_invalid = {}
        self.tag_missing = {}
        self.config  = map.config
        self.tag_invalid = {}
        self.tag_missing = {}
+       self.tag_reqerror = {}
        self.tag_error = {}
        self.deps = {}
        self.tag_error = {}
        self.deps = {}
-       self.cast = "string"
+       --self.cast = "string"
 
        self.track_missing = false
 
        self.track_missing = false
-       self.rmempty   = false
+       --self.rmempty   = false
        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.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.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
                                deps = _uvl_strip_remote_dependencies(deps)
                if vs.depends and not self.override_dependencies then
                        for i, deps in ipairs(vs.depends) do
                                deps = _uvl_strip_remote_dependencies(deps)
@@ -803,13 +906,9 @@ function AbstractValue.__init__(self, map, section, option, ...)
                                end
                        end
                end
                                end
                        end
                end
-               
-               if self.value and vs.values and not self.override_values then
-                       for k, v in pairs(vs.values) do
-                               self:value(k, v)
-                       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
@@ -821,7 +920,7 @@ function AbstractValue.depends(self, field, value)
        else
                deps = field
        end
        else
                deps = field
        end
-       
+
        table.insert(self.deps, {deps=deps, add=""})
 end
 
        table.insert(self.deps, {deps=deps, add=""})
 end
 
@@ -853,7 +952,7 @@ function AbstractValue.parse(self, section)
        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))
                if not fvalue then
                        self.tag_invalid[section] = true
                fvalue = self:transform(self:validate(fvalue, section))
                if not fvalue then
                        self.tag_invalid[section] = true
@@ -903,7 +1002,9 @@ 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
        elseif self.cast == "string" then
                if type(value) == "table" then
@@ -1012,18 +1113,42 @@ ListValue = class(AbstractValue)
 function ListValue.__init__(self, ...)
        AbstractValue.__init__(self, ...)
        self.template  = "cbi/lvalue"
 function ListValue.__init__(self, ...)
        AbstractValue.__init__(self, ...)
        self.template  = "cbi/lvalue"
+
        self.keylist = {}
        self.vallist = {}
        self.keylist = {}
        self.vallist = {}
-
        self.size   = 1
        self.widget = "select"
 end
 
        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.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[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(v.value, v.title or v.value, unpack(deps))
+                       end
+               end
+       end
+end
+
 function ListValue.value(self, key, val, ...)
 function ListValue.value(self, key, val, ...)
+       if luci.util.contains(self.keylist, key) then
+               return
+       end
+
        val = val or key
        table.insert(self.keylist, tostring(key))
        table.insert(self.vallist, tostring(val))
        val = val or key
        table.insert(self.keylist, tostring(key))
        table.insert(self.vallist, tostring(val))
-       
+
        for i, deps in ipairs({...}) do
                table.insert(self.deps, {add = "-"..key, deps=deps})
        end
        for i, deps in ipairs({...}) do
                table.insert(self.deps, {add = "-"..key, deps=deps})
        end
@@ -1049,6 +1174,7 @@ MultiValue = class(AbstractValue)
 function MultiValue.__init__(self, ...)
        AbstractValue.__init__(self, ...)
        self.template = "cbi/mvalue"
 function MultiValue.__init__(self, ...)
        AbstractValue.__init__(self, ...)
        self.template = "cbi/mvalue"
+
        self.keylist = {}
        self.vallist = {}
 
        self.keylist = {}
        self.vallist = {}
 
@@ -1065,6 +1191,10 @@ function MultiValue.render(self, ...)
 end
 
 function MultiValue.value(self, key, val)
 end
 
 function MultiValue.value(self, key, val)
+       if luci.util.contains(self.keylist, key) then
+               return
+       end
+
        val = val or key
        table.insert(self.keylist, tostring(key))
        table.insert(self.vallist, tostring(val))
        val = val or key
        table.insert(self.keylist, tostring(key))
        table.insert(self.vallist, tostring(val))
@@ -1101,6 +1231,16 @@ function StaticList.__init__(self, ...)
        MultiValue.__init__(self, ...)
        self.cast = "table"
        self.valuelist = self.cfgvalue
        MultiValue.__init__(self, ...)
        self.cast = "table"
        self.valuelist = self.cfgvalue
+
+       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
+                       for k, v in pairs(vs.values) do
+                               self:value(k, v)
+                       end
+               end
+       end
 end
 
 function StaticList.validate(self, value)
 end
 
 function StaticList.validate(self, value)
@@ -1108,7 +1248,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.vallist, v) then
                        table.insert(valid, v)
                end
        end
                        table.insert(valid, v)
                end
        end
@@ -1122,7 +1262,6 @@ function DynamicList.__init__(self, ...)
        AbstractValue.__init__(self, ...)
        self.template  = "cbi/dynlist"
        self.cast = "table"
        AbstractValue.__init__(self, ...)
        self.template  = "cbi/dynlist"
        self.cast = "table"
-       
        self.keylist = {}
        self.vallist = {}
 end
        self.keylist = {}
        self.vallist = {}
 end
@@ -1133,17 +1272,19 @@ 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.formvalue(self, section)
+       local value = AbstractValue.formvalue(self, section)
        value = (type(value) == "table") and value or {value}
        value = (type(value) == "table") and value or {value}
-       
+
        local valid = {}
        for i, v in ipairs(value) do
        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 luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
+                and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
                        table.insert(valid, v)
                end
        end
                        table.insert(valid, v)
                end
        end
-       
+
        return valid
 end
 
        return valid
 end
 
@@ -1169,4 +1310,4 @@ function Button.__init__(self, ...)
        self.template  = "cbi/button"
        self.inputstyle = nil
        self.rmempty = true
        self.template  = "cbi/button"
        self.inputstyle = nil
        self.rmempty = true
-end
\ No newline at end of file
+end