libs/cbi: implement tabbing to split large sections and group options in tabs
[project/luci.git] / libs / cbi / luasrc / cbi.lua
index e513053..33b0c7c 100644 (file)
@@ -30,9 +30,10 @@ require("luci.template")
 local util = require("luci.util")
 require("luci.http")
 require("luci.uvl")
-require("luci.fs")
+
 
 --local event      = require "luci.sys.event"
+local fs         = require("nixio.fs")
 local uci        = require("luci.model.uci")
 local class      = util.class
 local instanceof = util.instanceof
@@ -52,24 +53,25 @@ REMOVE_PREFIX = "cbi.rts."
 
 -- Loads a CBI map from given file, creating an environment and returns it
 function load(cbimap, ...)
-       require("luci.fs")
+       local fs   = require "nixio.fs"
        local i18n = require "luci.i18n"
        require("luci.config")
        require("luci.util")
 
        local upldir = "/lib/uci/upload/"
        local cbidir = luci.util.libpath() .. "/model/cbi/"
-
-       assert(luci.fs.stat(cbimap) or
-               luci.fs.stat(cbidir..cbimap..".lua") or
-               luci.fs.stat(cbidir..cbimap..".lua.gz"),
-                       "Model not found!")
-
-       local func, err = loadfile(cbimap)
-       if not func then
-               func, err = loadfile(cbidir..cbimap..".lua") or
-                       loadfile(cbidir..cbimap..".lua.gz")
+       local func, err
+
+       if fs.access(cbimap) then
+               func, err = loadfile(cbimap)
+       elseif 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")
+       else
+               func, err = nil, "Model '" .. cbimap .. "' not found!"
        end
+
        assert(func, err)
 
        luci.i18n.loadc("cbi")
@@ -285,6 +287,11 @@ function Template.render(self)
        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
@@ -498,6 +505,7 @@ function Delegator.__init__(self, ...)
        self.defaultpath = {}
        self.pageaction = false
        self.readinput = true
+       self.allow_reset = false
        self.allow_back = false
        self.allow_finish = false
        self.template = "cbi/delegator"
@@ -571,9 +579,14 @@ function Delegator.parse(self, ...)
                else
                        newcurrent = self:get_next(self.current)
                end
+       elseif stat < FORM_PROCEED then
+               return stat
        end
+       
 
-       if not newcurrent or not self:get(newcurrent) then
+       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
@@ -768,6 +781,19 @@ function AbstractSection.__init__(self, map, sectiontype, ...)
        self.dynamic = false
 end
 
+-- Define a tab for the section
+function AbstractSection.tab(self, tab, title, desc)
+       self.tabs      = self.tabs      or { }
+       self.tab_names = self.tab_names or { }
+
+       self.tab_names[#self.tab_names+1] = tab
+       self.tabs[tab] = {
+               title       = title,
+               description = desc,
+               childs      = { }
+       }
+end
+
 -- Appends a new option
 function AbstractSection.option(self, class, option, ...)
        -- Autodetect from UVL
@@ -799,6 +825,31 @@ function AbstractSection.option(self, class, option, ...)
        end
 end
 
+-- Appends a new tabbed option
+function AbstractSection.taboption(self, tab, ...)
+
+       assert(tab and self.tabs and self.tabs[tab],
+               "Cannot assign option to not existing tab %q" % tostring(tab))
+
+       local l = self.tabs[tab].childs
+       local o = AbstractSection.option(self, ...)
+
+       if o then l[#l+1] = o end
+
+       return o
+end
+
+-- Render a single tab
+function AbstractSection.render_tab(self, tab, ...)
+
+       assert(tab and self.tabs and self.tabs[tab],
+               "Cannot render not existing tab %q" % tostring(tab))
+
+       for _, node in ipairs(self.tabs[tab].childs) do
+               node:render(...)
+       end
+end
+
 -- Parse optional options
 function AbstractSection.parse_optionals(self, section)
        if not self.optional then
@@ -1327,6 +1378,7 @@ function AbstractValue.render(self, s, scope)
                scope.section   = s
                scope.cbid      = self:cbid(s)
                scope.striptags = luci.util.striptags
+               scope.pcdata    = luci.util.pcdata
 
                scope.ifattr = function(cond,key,val)
                        if cond then
@@ -1703,7 +1755,7 @@ end
 
 function FileUpload.cfgvalue(self, section)
        local val = AbstractValue.cfgvalue(self, section)
-       if val and luci.fs.access(val) then
+       if val and fs.access(val) then
                return val
        end
        return nil
@@ -1717,7 +1769,7 @@ function FileUpload.formvalue(self, section)
                then
                        return val
                end
-               luci.fs.unlink(val)
+               fs.unlink(val)
                self.value = nil
        end
        return nil
@@ -1725,7 +1777,7 @@ end
 
 function FileUpload.remove(self, section)
        local val = AbstractValue.formvalue(self, section)
-       if val and luci.fs.access(val) then luci.fs.unlink(val) end
+       if val and fs.access(val) then fs.unlink(val) end
        return AbstractValue.remove(self, section)
 end