lib/cbi: Added support for multiple CBI maps per model
[project/luci.git] / libs / cbi / luasrc / cbi.lua
index 31fa8bf..7370a04 100644 (file)
@@ -31,9 +31,11 @@ require("luci.util")
 require("luci.http")
 require("luci.model.uci")
 
+local uci        = luci.model.uci
 local class      = luci.util.class
 local instanceof = luci.util.instanceof
 
+
 -- Loads a CBI map from given file, creating an environment and returns it
 function load(cbimap)
        require("luci.fs")
@@ -55,14 +57,16 @@ function load(cbimap)
        luci.util.extfenv(func, "translate", luci.i18n.translate)
        luci.util.extfenv(func, "translatef", luci.i18n.translatef)
 
-       local map = func()
+       local maps = {func()}
 
-       if not instanceof(map, Map) then
-               error("CBI map returns no valid map object!")
-               return nil
+       for i, map in ipairs(maps) do
+               if not instanceof(map, Map) then
+                       error("CBI map returns no valid map object!")
+                       return nil
+               end
        end
 
-       return map
+       return maps
 end
 
 -- Node pseudo abstract class
@@ -141,27 +145,22 @@ function Map.__init__(self, config, ...)
 
        self.config = config
        self.template = "cbi/map"
-       self.uci = luci.model.uci.Session()
-       self.ucidata, self.uciorder = self.uci:sections(self.config)
-       if not self.ucidata or not self.uciorder then
+       if not uci.load(self.config) then
                error("Unable to read UCI data: " .. self.config)
        end
 end
 
 -- Use optimized UCI writing
 function Map.parse(self, ...)
-       self.uci:t_load(self.config)
        Node.parse(self, ...)
-       self.uci:t_save(self.config)
+       uci.save(self.config)
+       uci.unload(self.config)
 end
 
 -- Creates a child section
-function Map.section(self, class, section, ...)
+function Map.section(self, class, ...)
        if instanceof(class, AbstractSection) then
-               local obj  = class(self, section, ...)
-
-               Node._i18n(obj, self.config, section, nil, ...)
-
+               local obj  = class(self, ...)
                self:append(obj)
                return obj
        else
@@ -171,59 +170,35 @@ end
 
 -- UCI add
 function Map.add(self, sectiontype)
-       local name = self.uci:t_add(self.config, sectiontype)
-       if name then
-               self.ucidata[name] = {}
-               self.ucidata[name][".type"] = sectiontype
-               table.insert(self.uciorder, name)
-       end
-       return name
+       return uci.add(self.config, sectiontype)
 end
 
 -- UCI set
 function Map.set(self, section, option, value)
-       local stat = self.uci:t_set(self.config, section, option, value)
-       if stat then
-               local val = self.uci:t_get(self.config, section, option)
-               if option then
-                       self.ucidata[section][option] = val
-               else
-                       if not self.ucidata[section] then
-                               self.ucidata[section] = {}
-                       end
-                       self.ucidata[section][".type"] = val
-                       table.insert(self.uciorder, section)
-               end
+       if option then
+               return uci.set(self.config, section, option, value)
+       else
+               return uci.set(self.config, section, value)
        end
-       return stat
 end
 
 -- UCI del
 function Map.del(self, section, option)
-       local stat = self.uci:t_del(self.config, section, option)
-       if stat then
-               if option then
-                       self.ucidata[section][option] = nil
-               else
-                       self.ucidata[section] = nil
-                       for i, k in ipairs(self.uciorder) do
-                               if section == k then
-                                       table.remove(self.uciorder, i)
-                               end
-                       end
-               end
+       if option then
+               return uci.delete(self.config, section, option)
+       else
+               return uci.delete(self.config, section)
        end
-       return stat
 end
 
--- UCI get (cached)
+-- UCI get
 function Map.get(self, section, option)
        if not section then
-               return self.ucidata, self.uciorder
-       elseif option and self.ucidata[section] then
-               return self.ucidata[section][option]
+               return uci.get_all(self.config)
+       elseif option then
+               return uci.get(self.config, section, option)
        else
-               return self.ucidata[section]
+               return uci.get_all(self.config, section)
        end
 end
 
@@ -250,7 +225,7 @@ function AbstractSection.option(self, class, option, ...)
        if instanceof(class, AbstractValue) then
                local obj  = class(self.map, option, ...)
 
-               Node._i18n(obj, self.config, self.section, option, ...)
+               Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
 
                self:append(obj)
                return obj
@@ -338,10 +313,11 @@ NamedSection - A fixed configuration section defined by its name
 ]]--
 NamedSection = class(AbstractSection)
 
-function NamedSection.__init__(self, map, section, ...)
-       AbstractSection.__init__(self, map, ...)
+function NamedSection.__init__(self, map, section, type, ...)
+       AbstractSection.__init__(self, map, type, ...)
+       Node._i18n(self, map.config, section, nil, ...)
+       
        self.template = "cbi/nsection"
-
        self.section = section
        self.addremove = false
 end
@@ -384,8 +360,10 @@ TypedSection - A (set of) configuration section(s) defined by the type
 ]]--
 TypedSection = class(AbstractSection)
 
-function TypedSection.__init__(self, ...)
-       AbstractSection.__init__(self, ...)
+function TypedSection.__init__(self, map, type, ...)
+       AbstractSection.__init__(self, map, type, ...)
+       Node._i18n(self, map.config, type, nil, ...)
+
        self.template  = "cbi/tsection"
        self.deps = {}
        self.excludes = {}
@@ -396,15 +374,12 @@ end
 -- Return all matching UCI sections for this TypedSection
 function TypedSection.cfgsections(self)
        local sections = {}
-       local map, order = self.map:get()
-
-       for i, k in ipairs(order) do
-               if map[k][".type"] == self.sectiontype then
-                       if self:checkscope(k) then
-                               table.insert(sections, k)
-                       end
-               end
-       end
+       uci.foreach(self.map.config, self.sectiontype,
+               function (section)
+                       if self:checkscope(section[".name"]) then
+                               table.insert(sections, section[".name"])
+                       end 
+               end)
 
        return sections
 end
@@ -579,6 +554,7 @@ function AbstractValue.render(self, s, scope)
        if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
                scope = scope or {}
                scope.section = s
+               
                Node.render(self, scope)
        end
 end
@@ -609,9 +585,6 @@ end
 --[[
 Value - A one-line value
        maxlength:      The maximum length
-       isnumber:       The value must be a valid (floating point) number
-       isinteger:  The value must be a valid integer
-       ispositive: The value must be positive (and a number)
 ]]--
 Value = class(AbstractValue)
 
@@ -620,8 +593,6 @@ function Value.__init__(self, ...)
        self.template  = "cbi/value"
 
        self.maxlength  = nil
-       self.isnumber   = false
-       self.isinteger  = false
 end
 
 -- This validation is a bit more complex
@@ -630,7 +601,7 @@ function Value.validate(self, val)
                val = nil
        end
 
-       return luci.util.validate(val, self.isnumber, self.isinteger)
+       return val
 end
 
 
@@ -735,6 +706,14 @@ function MultiValue.__init__(self, ...)
        self.delimiter = " "
 end
 
+function MultiValue.render(self, ...)
+       if self.widget == "select" and not self.size then
+               self.size = #self.vallist
+       end
+
+       AbstractValue.render(self, ...)
+end
+
 function MultiValue.value(self, key, val)
        val = val or key
        table.insert(self.keylist, tostring(key))
@@ -752,21 +731,15 @@ function MultiValue.valuelist(self, section)
 end
 
 function MultiValue.validate(self, val)
-       if not(type(val) == "string") then
-               return nil
-       end
+       val = (type(val) == "table") and val or {val} 
 
-       local result = ""
+       local result
 
-       for value in val:gmatch("[^\n]+") do
+       for i, value in ipairs(val) do
                if luci.util.contains(self.keylist, value) then
-                       result = result .. self.delimiter .. value
+                       result = result and (result .. self.delimiter .. value) or value
                end
        end
 
-       if result:len() > 0 then
-               return result:sub(self.delimiter:len() + 1)
-       else
-               return nil
-       end
+       return result
 end