libs/web: better server side handling of DynList values
[oweals/luci.git] / libs / web / luasrc / cbi.lua
index 0a63d6090a8ffddcd9fb862b630d10f49cd53d42..0fc93b25cd3dce1fa8f504fe743d0fccbfe3c405 100644 (file)
@@ -304,9 +304,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
@@ -1279,7 +1281,7 @@ function AbstractValue.parse(self, section, novld)
                        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
@@ -1356,20 +1358,35 @@ 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
 
@@ -1402,6 +1419,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))
@@ -1487,6 +1509,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
@@ -1537,6 +1564,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
@@ -1613,31 +1645,75 @@ 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
+       elseif self.cast == "table" then
+               local x
+               for x in util.imatch(value) do
+                       t[#t+1] = x
+               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
+               local x
+               local t = { }
+               for x in value:gmatch("%S+") do
+                       t[#t+1] = x
                end
+               value = t
        end
 
-       return valid
+       return value
 end