libs/cbi: don't run apply on render, this is a quick hack and will be reworked soon
[project/luci.git] / libs / cbi / luasrc / cbi.lua
index e265589..8dd16b1 100644 (file)
@@ -29,12 +29,12 @@ module("luci.cbi", package.seeall)
 require("luci.template")
 local util = require("luci.util")
 require("luci.http")
-require("luci.uvl")
 
 
 --local event      = require "luci.sys.event"
 local fs         = require("nixio.fs")
 local uci        = require("luci.model.uci")
+local datatypes  = require("luci.cbi.datatypes")
 local class      = util.class
 local instanceof = util.instanceof
 
@@ -62,12 +62,10 @@ function load(cbimap, ...)
        local cbidir = luci.util.libpath() .. "/model/cbi/"
        local func, err
 
-       if fs.access(cbimap) then
-               func, err = loadfile(cbimap)
-       elseif fs.access(cbidir..cbimap..".lua") then
+       if fs.access(cbidir..cbimap..".lua") then
                func, err = loadfile(cbidir..cbimap..".lua")
-       elseif fs.access(cbidir..cbimap..".lua.gz") then
-               func, err = loadfile(cbidir..cbimap..".lua.gz")
+       elseif fs.access(cbimap) then
+               func, err = loadfile(cbimap)
        else
                func, err = nil, "Model '" .. cbimap .. "' not found!"
        end
@@ -152,64 +150,6 @@ function load(cbimap, ...)
        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 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
-
-       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 = {}
-
-       for k, v in pairs(deps) do
-               k = k:gsub("%$config%.%$section%.", "")
-               if k:match("^[%w_]+$") and type(v) == "string" then
-                       clean[k] = v
-               end
-       end
-
-       return clean
-end
-
 
 -- Node pseudo abstract class
 Node = class()
@@ -225,7 +165,7 @@ end
 function Node._run_hook(self, hook)
        if type(self[hook]) == "function" then
                return self[hook](self)
-       end 
+       end
 end
 
 function Node._run_hooks(self, ...)
@@ -319,9 +259,6 @@ function Map.__init__(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)
@@ -419,7 +356,7 @@ end
 function Map.render(self, ...)
        self:_run_hooks("on_init")
        Node.render(self, ...)
-       if self._apply then
+       if false and self._apply then
                local fp = self._apply()
                fp:read("*a")
                fp:close()
@@ -561,7 +498,7 @@ function Delegator.get(self, name)
        local node = self.nodes[name]
 
        if type(node) == "string" then
-               node = load(node)
+               node = load(node, name)
        end
 
        if type(node) == "table" and getmetatable(node) == nil then
@@ -577,7 +514,7 @@ function Delegator.parse(self, ...)
                        return FORM_DONE
                end
        end
-       
+
        if not Map.formvalue(self, "cbi.delg.current") then
                self:_run_hooks("on_init")
        end
@@ -587,11 +524,11 @@ function Delegator.parse(self, ...)
        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() 
+               stat = self.active:parse()
        else
                self:active()
        end
@@ -605,11 +542,11 @@ function Delegator.parse(self, ...)
        elseif stat < FORM_PROCEED then
                return stat
        end
-       
+
 
        if not Map.formvalue(self, "cbi.submit") then
                return FORM_NODATA
-       elseif stat > FORM_PROCEED 
+       elseif stat > FORM_PROCEED
        and (not newcurrent or not self:get(newcurrent)) then
                return self:_run_hook("on_done") or FORM_DONE
        else
@@ -617,8 +554,12 @@ function Delegator.parse(self, ...)
                self.active = self:get(self.current)
                if type(self.active) ~= "function" then
                        self.active:populate_delegator(self)
-                       self.active:parse(false)
-                       return FROM_PROCEED
+                       local stat = self.active:parse(false)
+                       if stat == FORM_SKIP then
+                               return self:parse(...)
+                       else
+                               return FORM_PROCEED
+                       end
                else
                        return self:parse(...)
                end
@@ -823,22 +764,13 @@ function AbstractSection.tab(self, tab, title, desc)
        }
 end
 
+-- Check whether the section has tabs
+function AbstractSection.has_tabs(self)
+       return (self.tabs ~= nil) and (next(self.tabs) ~= nil)
+end
+
 -- Appends a new option
 function AbstractSection.option(self, class, option, ...)
-       -- 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
-                       class = Flag
-               elseif vs.type == "list" then
-                       class = DynamicList
-               elseif vs.type == "enum" or vs.type == "reference" then
-                       class = ListValue
-               else
-                       class = Value
-               end
-       end
-
        if instanceof(class, AbstractValue) then
                local obj  = class(self.map, self, option, ...)
                self:append(obj)
@@ -886,7 +818,7 @@ function AbstractSection.parse_optionals(self, 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 v.optional and not v:cfgvalue(section) and not self:has_tabs() then
                        if field == v.option then
                                field = nil
                                self.map.proceed = true
@@ -1058,16 +990,6 @@ function NamedSection.__init__(self, map, section, stype, ...)
 
        -- Defaults
        self.addremove = false
-
-       -- Use defaults from UVL
-       if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
-               local vs = self.map:get_scheme(self.sectiontype)
-               self.addremove = not vs.unique and not vs.required
-               self.dynamic   = vs.dynamic
-               self.title       = self.title or vs.title
-               self.description = self.description or vs.descr
-       end
-
        self.template = "cbi/nsection"
        self.section = section
 end
@@ -1095,10 +1017,6 @@ function NamedSection.parse(self, novld)
                AbstractSection.parse_dynamic(self, s)
                if self.map:submitstate() 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)
 
@@ -1120,19 +1038,9 @@ TypedSection = class(AbstractSection)
 function TypedSection.__init__(self, map, type, ...)
        AbstractSection.__init__(self, map, type, ...)
 
-       self.template  = "cbi/tsection"
+       self.template = "cbi/tsection"
        self.deps = {}
        self.anonymous = false
-
-       -- Use defaults from UVL
-       if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
-               local vs = self.map:get_scheme(self.sectiontype)
-               self.addremove = not vs.unique and not vs.required
-               self.dynamic   = vs.dynamic
-               self.anonymous = not vs.named
-               self.title       = self.title or vs.title
-               self.description = self.description or vs.descr
-       end
 end
 
 -- Return all matching UCI sections for this TypedSection
@@ -1173,10 +1081,6 @@ function TypedSection.parse(self, novld)
                AbstractSection.parse_dynamic(self, k)
                if self.map:submitstate() then
                        Node.parse(self, k, novld)
-
-                       if not novld and not self.override_scheme and self.map.scheme then
-                               _uvl_validate_section(self, k)
-                       end
                end
                AbstractSection.parse_optionals(self, k)
        end
@@ -1288,29 +1192,6 @@ function AbstractValue.__init__(self, map, section, option, ...)
 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)
-               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
-               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 next(deps) then
-                                       self:depends(deps)
-                               end
-                       end
-               end
-       end
-
        self.cast = self.cast or "string"
 end
 
@@ -1383,7 +1264,7 @@ function AbstractValue.parse(self, section, novld)
                                table.insert(self.section.error[section], "invalid")
                        else
                                self.section.error = {[section] = {"invalid"}}
-                       end 
+                       end
                        self.map.save = false
                end
                if fvalue and not (fvalue == cvalue) then
@@ -1414,7 +1295,7 @@ end
 
 -- Render if this value exists or if it is mandatory
 function AbstractValue.render(self, s, scope)
-       if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
+       if not self.optional or self.section:has_tabs() or self:cfgvalue(s) or self:formcreated(s) then
                scope = scope or {}
                scope.section   = s
                scope.cbid      = self:cbid(s)
@@ -1445,7 +1326,8 @@ end
 
 -- Return the UCI value of this object
 function AbstractValue.cfgvalue(self, section)
-       local value = self.map:get(section, self.option)
+       local value = (self.error and self.error[section] == "invalid")
+               and self:formvalue(section) or self.map:get(section, self.option)
        if not value then
                return nil
        elseif not self.cast or self.cast == type(value) then
@@ -1461,6 +1343,20 @@ end
 
 -- Validate the form value
 function AbstractValue.validate(self, value)
+       if self.datatype and value and datatypes[self.datatype] then
+               if type(value) == "table" then
+                       local v
+                       for _, v in ipairs(value) do
+                               if v and #v > 0 and not datatypes[self.datatype](v) then
+                                       return nil
+                               end
+                       end
+               else
+                       if not datatypes[self.datatype](value) then
+                               return nil
+                       end
+               end
+       end
        return value
 end
 
@@ -1578,26 +1474,6 @@ function ListValue.__init__(self, ...)
        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, ...)
        if luci.util.contains(self.keylist, key) then
                return