X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=libs%2Fweb%2Fluasrc%2Fcbi.lua;h=5aa2f5a092ef604c8628d1e1b96ec1455fd70ff9;hb=bf49f78599a006a9d136d9fd83cecc5d8a5afb1a;hp=993b422f660131bbc43aef105575df514417dd3c;hpb=17575b4d75cd9fede5ef0f3bfc7f9244fdb2ac06;p=oweals%2Fluci.git diff --git a/libs/web/luasrc/cbi.lua b/libs/web/luasrc/cbi.lua index 993b422f6..5aa2f5a09 100644 --- a/libs/web/luasrc/cbi.lua +++ b/libs/web/luasrc/cbi.lua @@ -50,6 +50,8 @@ AUTO = true CREATE_PREFIX = "cbi.cts." REMOVE_PREFIX = "cbi.rts." +RESORT_PREFIX = "cbi.sts." +FEXIST_PREFIX = "cbi.cbe." -- Loads a CBI map from given file, creating an environment and returns it function load(cbimap, ...) @@ -209,7 +211,9 @@ end -- Render the children function Node.render_children(self, ...) + local k, node for k, node in ipairs(self.children) do + node.last_child = (k == #self.children) node:render(...) end end @@ -304,9 +308,11 @@ function Map.parse(self, readinput, ...) Node.parse(self, ...) if self.save then + self:_run_hooks("on_save", "on_before_save") for i, config in ipairs(self.parsechain) do self.uci:save(config) end + self:_run_hooks("on_after_save") 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 @@ -320,10 +326,10 @@ function Map.parse(self, readinput, ...) 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) - return io.popen(cmd) - end + -- This is evaluated by the dispatcher and delegated to the + -- template which in turn fires XHR to perform the actual + -- apply actions. + self.apply_needed = true end -- Reparse sections @@ -356,12 +362,6 @@ end function Map.render(self, ...) self:_run_hooks("on_init") Node.render(self, ...) - if false and self._apply then - local fp = self._apply() - fp:read("*a") - fp:close() - self:_run_hooks("on_apply") - end end -- Creates a child section @@ -382,10 +382,14 @@ end -- UCI set function Map.set(self, section, option, value) - if option then - return self.uci:set(self.config, section, option, value) + if type(value) ~= "table" or #value > 0 then + if option then + return self.uci:set(self.config, section, option, value) + else + return self.uci:set(self.config, section, value) + end else - return self.uci:set(self.config, section, value) + return Map.del(self, section, option) end end @@ -803,7 +807,9 @@ 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 + local k, node + for k, node in ipairs(self.tabs[tab].childs) do + node.last_child = (k == #self.tabs[tab].childs) node:render(...) end end @@ -1121,6 +1127,20 @@ function TypedSection.parse(self, novld) end end + if self.sortable then + local stval = RESORT_PREFIX .. self.config .. "." .. self.sectiontype + local order = self.map:formvalue(stval) + if order and #order > 0 then + local sid + local num = 0 + for sid in util.imatch(order) do + self.map.uci:reorder(self.config, sid, num) + num = num + 1 + end + self.changed = (num > 0) + end + end + if created or self.changed then self:push_events() end @@ -1232,6 +1252,24 @@ function AbstractValue.mandatory(self, value) self.rmempty = not value end +function AbstractValue.add_error(self, section, type, msg) + self.error = self.error or { } + self.error[section] = msg or type + + self.section.error = self.section.error or { } + self.section.error[section] = self.section.error[section] or { } + table.insert(self.section.error[section], msg or type) + + if type == "invalid" then + self.tag_invalid[section] = true + elseif type == "missing" then + self.tag_missing[section] = true + end + + self.tag_error[section] = true + self.map.save = false +end + function AbstractValue.parse(self, section, novld) local fvalue = self:formvalue(section) local cvalue = self:cfgvalue(section) @@ -1258,18 +1296,10 @@ function AbstractValue.parse(self, section, novld) fvalue = self:transform(fvalue) if not fvalue and not novld then - val_err = val_err or "invalid" - - self.error = self.error or { } - self.error[section] = val_err - - self.section.error = self.section.error or { } - self.section.error[section] = self.section.error[section] or { } - table.insert(self.section.error[section], val_err) - - self.map.save = false + self:add_error(section, "invalid", val_err) end - if fvalue and not (fvalue == cvalue) then + + if fvalue and (self.forcewrite or not (fvalue == cvalue)) then if self:write(section, fvalue) then -- Push events self.section.changed = true @@ -1284,13 +1314,9 @@ function AbstractValue.parse(self, section, novld) --luci.util.append(self.map.events, self.events) end elseif cvalue ~= fvalue and not novld then - self:write(section, fvalue or "") - if self.error then - self.error[section] = "missing" - else - self.error = { [section] = "missing" } - end - self.map.save = false + -- trigger validator with nil value to get custom user error msg. + local _, val_err = self:validate(nil, section) + self:add_error(section, "missing", val_err) end end end @@ -1328,8 +1354,13 @@ end -- Return the UCI value of this object function AbstractValue.cfgvalue(self, section) - local value = (self.error and self.error[section] == "invalid") - and self:formvalue(section) or self.map:get(section, self.option) + local value + if self.tag_error[section] then + value = self:formvalue(section) + else + value = self.map:get(section, self.option) + end + if not value then return nil elseif not self.cast or self.cast == type(value) then @@ -1339,26 +1370,41 @@ function AbstractValue.cfgvalue(self, section) return value[1] end elseif self.cast == "table" then - return luci.util.split(value, "%s+", nil, true) + return { value } end 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 + if self.datatype and value then + local args = { } + local dt, ar = self.datatype:match("^(%w+)%(([^%(%)]+)%)") + + if dt and ar then + local a + for a in ar:gmatch("[^%s,]+") do + args[#args+1] = a end else - if not datatypes[self.datatype](value) then - return nil + dt = self.datatype + end + + if dt and datatypes[dt] then + if type(value) == "table" then + local v + for _, v in ipairs(value) do + if v and #v > 0 and not datatypes[dt](v, unpack(args)) then + return nil + end + end + else + if not datatypes[dt](value, unpack(args)) then + return nil + end end end end + return value end @@ -1391,6 +1437,11 @@ function Value.__init__(self, ...) self.vallist = {} end +function Value.reset_values(self) + self.keylist = {} + self.vallist = {} +end + function Value.value(self, key, val) val = val or key table.insert(self.keylist, tostring(key)) @@ -1435,29 +1486,31 @@ function Flag.__init__(self, ...) AbstractValue.__init__(self, ...) self.template = "cbi/fvalue" - self.enabled = "1" + self.enabled = "1" self.disabled = "0" + self.default = self.disabled end -- A flag can only have two states: set or unset function Flag.parse(self, section) - local fvalue = self:formvalue(section) - - if fvalue then - fvalue = self.enabled - else - fvalue = self.disabled - end + local fexists = self.map:formvalue( + FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option) - if fvalue == self.enabled or (not self.optional and not self.rmempty) then - if not(fvalue == self:cfgvalue(section)) then + if fexists then + local fvalue = self:formvalue(section) and self.enabled or self.disabled + if fvalue ~= self.default or (not self.optional and not self.rmempty) then self:write(section, fvalue) + else + self:remove(section) end else self:remove(section) end end +function Flag.cfgvalue(self, section) + return AbstractValue.cfgvalue(self, section) or self.default +end --[[ @@ -1476,6 +1529,11 @@ function ListValue.__init__(self, ...) self.widget = "select" end +function ListValue.reset_values(self) + self.keylist = {} + self.vallist = {} +end + function ListValue.value(self, key, val, ...) if luci.util.contains(self.keylist, key) then return @@ -1526,6 +1584,11 @@ function MultiValue.render(self, ...) AbstractValue.render(self, ...) end +function MultiValue.reset_values(self) + self.keylist = {} + self.vallist = {} +end + function MultiValue.value(self, key, val) if luci.util.contains(self.keylist, key) then return @@ -1602,31 +1665,74 @@ function DynamicList.__init__(self, ...) self.vallist = {} end +function DynamicList.reset_values(self) + self.keylist = {} + self.vallist = {} +end + function DynamicList.value(self, key, val) val = val or key table.insert(self.keylist, tostring(key)) table.insert(self.vallist, tostring(val)) end -function DynamicList.write(self, ...) - self.map.proceed = true - return AbstractValue.write(self, ...) +function DynamicList.write(self, section, value) + local t = { } + + if type(value) == "table" then + local x + for _, x in ipairs(value) do + if x and #x > 0 then + t[#t+1] = x + end + end + else + t = { value } + end + + if self.cast == "string" then + value = table.concat(t, " ") + else + value = t + end + + return AbstractValue.write(self, section, value) +end + +function DynamicList.cfgvalue(self, section) + local value = AbstractValue.cfgvalue(self, section) + + if type(value) == "string" then + local x + local t = { } + for x in value:gmatch("%S+") do + if #x > 0 then + t[#t+1] = x + end + end + value = t + end + + return value end function DynamicList.formvalue(self, section) local value = AbstractValue.formvalue(self, section) - value = (type(value) == "table") and value or {value} - local valid = {} - for i, v in ipairs(value) do - if v and #v > 0 - and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i) - and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then - table.insert(valid, v) + if type(value) == "string" then + if self.cast == "string" then + local x + local t = { } + for x in value:gmatch("%S+") do + t[#t+1] = x + end + value = t + else + value = { value } end end - return valid + return value end