CBI: Allow lazy loading of wizard pages (improving RAM usage)
[project/luci.git] / libs / cbi / luasrc / cbi.lua
index 1b22db0a8adf5a3837e9cd28120f2e90dfdc2ad4..9b947cb2f0fe841dacab2316c334e6732db2c54c 100644 (file)
@@ -74,8 +74,7 @@ function load(cbimap, ...)
 
        assert(func, err)
 
-       luci.i18n.loadc("cbi")
-       luci.i18n.loadc("uvl")
+       luci.i18n.loadc("base")
 
        local env = {
                translate=i18n.translate,
@@ -222,20 +221,23 @@ function Node.__init__(self, title, description)
        self.template = "cbi/node"
 end
 
--- i18n helper
-function Node._i18n(self, config, section, option, title, description)
-
-       -- i18n loaded?
-       if type(luci.i18n) == "table" then
-
-               local key = config and config:gsub("[^%w]+", "") or ""
-
-               if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
-               if option  then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "")  end
+-- hook helper
+function Node._run_hook(self, hook)
+       if type(self[hook]) == "function" then
+               return self[hook](self)
+       end 
+end
 
-               self.title = title or luci.i18n.translate( key, option or section or config )
-               self.description = description or luci.i18n.translate( key .. "_desc", "" )
+function Node._run_hooks(self, ...)
+       local f
+       local r = false
+       for _, f in ipairs(arg) do
+               if type(self[f]) == "function" then
+                       self[f](self)
+                       r = true
+               end
        end
+       return r
 end
 
 -- Prepare nodes
@@ -300,7 +302,6 @@ Map = class(Node)
 
 function Map.__init__(self, config, ...)
        Node.__init__(self, ...)
-       Node._i18n(self, config, nil, nil, ...)
 
        self.config = config
        self.parsechain = {self.config}
@@ -321,7 +322,6 @@ function Map.__init__(self, config, ...)
 
        self.validator = luci.uvl.UVL()
        self.scheme = self.validator:get_scheme(self.config)
-
 end
 
 function Map.formvalue(self, key)
@@ -357,6 +357,7 @@ end
 -- Use optimized UCI writing
 function Map.parse(self, readinput, ...)
        self.readinput = (readinput ~= false)
+       self:_run_hooks("on_parse")
 
        if self:formvalue("cbi.skip") then
                self.state = FORM_SKIP
@@ -370,14 +371,17 @@ function Map.parse(self, readinput, ...)
                        self.uci:save(config)
                end
                if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
+                       self:_run_hooks("on_before_commit")
                        for i, config in ipairs(self.parsechain) do
                                self.uci:commit(config)
 
                                -- Refresh data because commit changes section names
                                self.uci:load(config)
                        end
+                       self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
                        if self.apply_on_parse then
                                self.uci:apply(self.parsechain)
+                               self:_run_hooks("on_apply", "on_after_apply")
                        else
                                self._apply = function()
                                        local cmd = self.uci:apply(self.parsechain, true)
@@ -413,11 +417,13 @@ function Map.parse(self, readinput, ...)
 end
 
 function Map.render(self, ...)
+       self:_run_hooks("on_init")
        Node.render(self, ...)
        if self._apply then
                local fp = self._apply()
                fp:read("*a")
                fp:close()
+               self:_run_hooks("on_apply")
        end
 end
 
@@ -506,16 +512,13 @@ function Delegator.__init__(self, ...)
        self.pageaction = false
        self.readinput = true
        self.allow_reset = false
+       self.allow_cancel = 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
@@ -527,9 +530,9 @@ function Delegator.add(self, name, node)
 end
 
 function Delegator.insert_after(self, name, after)
-       local n = #self.chain
+       local n = #self.chain + 1
        for k, v in ipairs(self.chain) do
-               if v == state then
+               if v == after then
                        n = k + 1
                        break
                end
@@ -555,10 +558,30 @@ function Delegator.set_route(self, ...)
 end
 
 function Delegator.get(self, name)
-       return self.nodes[name]
+       local node = self.nodes[name]
+
+       if type(node) == "string" then
+               node = load(node)
+       end
+
+       if type(node) == "table" and getmetatable(node) == nil then
+               node = Compound(unpack(node))
+       end
+
+       return node
 end
 
 function Delegator.parse(self, ...)
+       if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then
+               if self:_run_hooks("on_cancel") then
+                       return FORM_DONE
+               end
+       end
+       
+       if not Map.formvalue(self, "cbi.delg.current") then
+               self:_run_hooks("on_init")
+       end
+
        local newcurrent
        self.chain = self.chain or self:get_chain()
        self.current = self.current or self:get_active()
@@ -586,10 +609,11 @@ function Delegator.parse(self, ...)
 
        if not Map.formvalue(self, "cbi.submit") then
                return FORM_NODATA
-       elseif not newcurrent or not self:get(newcurrent) then
-               return FORM_DONE
+       elseif stat > FORM_PROCEED 
+       and (not newcurrent or not self:get(newcurrent)) then
+               return self:_run_hook("on_done") or FORM_DONE
        else
-               self.current = newcurrent
+               self.current = newcurrent or self.current
                self.active = self:get(self.current)
                if type(self.active) ~= "function" then
                        self.active:parse(false)
@@ -659,6 +683,10 @@ function SimpleForm.parse(self, readinput, ...)
                return FORM_SKIP
        end
 
+       if self:formvalue("cbi.cancel") and self:_run_hooks("on_cancel") then
+               return FORM_DONE
+       end
+
        if self:submitstate() then
                Node.parse(self, 1, ...)
        end
@@ -812,9 +840,6 @@ function AbstractSection.option(self, class, option, ...)
 
        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
@@ -1029,7 +1054,6 @@ NamedSection = class(AbstractSection)
 
 function NamedSection.__init__(self, map, section, stype, ...)
        AbstractSection.__init__(self, map, stype, ...)
-       Node._i18n(self, map.config, section, nil, ...)
 
        -- Defaults
        self.addremove = false
@@ -1094,7 +1118,6 @@ TypedSection = class(AbstractSection)
 
 function TypedSection.__init__(self, map, type, ...)
        AbstractSection.__init__(self, map, type, ...)
-       Node._i18n(self, map.config, type, nil, ...)
 
        self.template  = "cbi/tsection"
        self.deps = {}
@@ -1253,6 +1276,7 @@ function AbstractValue.__init__(self, map, section, option, ...)
        self.tag_reqerror = {}
        self.tag_error = {}
        self.deps = {}
+       self.subdeps = {}
        --self.cast = "string"
 
        self.track_missing = false
@@ -1583,7 +1607,7 @@ function ListValue.value(self, key, val, ...)
        table.insert(self.vallist, tostring(val))
 
        for i, deps in ipairs({...}) do
-               table.insert(self.deps, {add = "-"..key, deps=deps})
+               self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
        end
 end