2 LuCI - Configuration Bind Interface
5 Offers an interface for binding configuration values to certain
6 data types. Supports value and range validation and basic dependencies.
12 Copyright 2008 Steven Barth <steven@midlink.org>
14 Licensed under the Apache License, Version 2.0 (the "License");
15 you may not use this file except in compliance with the License.
16 You may obtain a copy of the License at
18 http://www.apache.org/licenses/LICENSE-2.0
20 Unless required by applicable law or agreed to in writing, software
21 distributed under the License is distributed on an "AS IS" BASIS,
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 See the License for the specific language governing permissions and
24 limitations under the License.
27 module("luci.cbi", package.seeall)
29 require("luci.template")
35 local uci = require("luci.model.uci")
36 local class = luci.util.class
37 local instanceof = luci.util.instanceof
45 CREATE_PREFIX = "cbi.cts."
46 REMOVE_PREFIX = "cbi.rts."
48 -- Loads a CBI map from given file, creating an environment and returns it
49 function load(cbimap, ...)
51 local i18n = require "luci.i18n"
52 require("luci.config")
55 local cbidir = luci.util.libpath() .. "/model/cbi/"
56 local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
59 luci.i18n.loadc("cbi")
60 luci.i18n.loadc("uvl")
63 translate=i18n.translate,
64 translatef=i18n.translatef,
68 setfenv(func, setmetatable(env, {__index =
70 return rawget(tbl, key) or _M[key] or _G[key]
75 for i, map in ipairs(maps) do
76 if not instanceof(map, Node) then
77 error("CBI map returns no valid map object!")
87 local function _uvl_validate_section(node, name)
88 local co = node.map:get()
90 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
91 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
93 local function tag_fields(e)
94 if e.option and node.fields[e.option] then
95 if node.fields[e.option].error then
96 node.fields[e.option].error[name] = e
98 node.fields[e.option].error = { [name] = e }
101 for _, c in ipairs(e.childs) do tag_fields(c) end
105 local function tag_section(e)
107 for _, c in ipairs(e.childs) do
108 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
109 table.insert( s, c.childs[1]:string() )
111 table.insert( s, c:string() )
118 node.error = { [name] = s }
123 local stat, err = node.map.validator:validate_section(node.config, name, co)
125 node.map.save = false
132 local function _uvl_strip_remote_dependencies(deps)
135 for k, v in pairs(deps) do
136 k = k:gsub("%$config%.%$section%.", "")
137 if k:match("^[%w_]+$") and type(v) == "string" then
146 -- Node pseudo abstract class
149 function Node.__init__(self, title, description)
151 self.title = title or ""
152 self.description = description or ""
153 self.template = "cbi/node"
157 function Node._i18n(self, config, section, option, title, description)
160 if type(luci.i18n) == "table" then
162 local key = config and config:gsub("[^%w]+", "") or ""
164 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
165 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
167 self.title = title or luci.i18n.translate( key, option or section or config )
168 self.description = description or luci.i18n.translate( key .. "_desc", "" )
173 function Node.prepare(self, ...)
174 for k, child in ipairs(self.children) do
179 -- Append child nodes
180 function Node.append(self, obj)
181 table.insert(self.children, obj)
184 -- Parse this node and its children
185 function Node.parse(self, ...)
186 for k, child in ipairs(self.children) do
192 function Node.render(self, scope)
196 luci.template.render(self.template, scope)
199 -- Render the children
200 function Node.render_children(self, ...)
201 for k, node in ipairs(self.children) do
208 A simple template element
210 Template = class(Node)
212 function Template.__init__(self, template)
214 self.template = template
217 function Template.render(self)
218 luci.template.render(self.template, {self=self})
223 Map - A map describing a configuration file
227 function Map.__init__(self, config, ...)
228 Node.__init__(self, ...)
229 Node._i18n(self, config, nil, nil, ...)
232 self.parsechain = {self.config}
233 self.template = "cbi/map"
234 self.apply_on_parse = nil
235 self.uci = uci.cursor()
237 if not self.uci:load(self.config) then
238 error("Unable to read UCI data: " .. self.config)
241 self.validator = luci.uvl.UVL()
242 self.scheme = self.validator:get_scheme(self.config)
246 function Map.get_scheme(self, sectiontype, option)
248 return self.scheme and self.scheme.sections[sectiontype]
250 return self.scheme and self.scheme.variables[sectiontype]
251 and self.scheme.variables[sectiontype][option]
256 -- Chain foreign config
257 function Map.chain(self, config)
258 table.insert(self.parsechain, config)
261 -- Use optimized UCI writing
262 function Map.parse(self)
266 for i, config in ipairs(self.parsechain) do
267 self.uci:save(config)
269 if luci.http.formvalue("cbi.apply") then
270 for i, config in ipairs(self.parsechain) do
271 self.uci:commit(config)
273 -- Refresh data because commit changes section names
274 self.uci:load(config)
276 if self.apply_on_parse then
277 self.uci:apply(self.parsechain)
279 self._apply = function()
280 local cmd = self.uci:apply(self.parsechain, true)
286 Node.parse(self, true)
289 for i, config in ipairs(self.parsechain) do
290 self.uci:unload(config)
292 if type(self.commit_handler) == "function" then
293 self:commit_handler()
298 function Map.render(self, ...)
299 Node.render(self, ...)
301 local fp = self._apply()
307 -- Creates a child section
308 function Map.section(self, class, ...)
309 if instanceof(class, AbstractSection) then
310 local obj = class(self, ...)
314 error("class must be a descendent of AbstractSection")
319 function Map.add(self, sectiontype)
320 return self.uci:add(self.config, sectiontype)
324 function Map.set(self, section, option, value)
326 return self.uci:set(self.config, section, option, value)
328 return self.uci:set(self.config, section, value)
333 function Map.del(self, section, option)
335 return self.uci:delete(self.config, section, option)
337 return self.uci:delete(self.config, section)
342 function Map.get(self, section, option)
344 return self.uci:get_all(self.config)
346 return self.uci:get(self.config, section, option)
348 return self.uci:get_all(self.config, section)
358 Page.__init__ = Node.__init__
359 Page.parse = function() end
363 SimpleForm - A Simple non-UCI form
365 SimpleForm = class(Node)
367 function SimpleForm.__init__(self, config, title, description, data)
368 Node.__init__(self, title, description)
370 self.data = data or {}
371 self.template = "cbi/simpleform"
375 function SimpleForm.parse(self, ...)
376 if luci.http.formvalue("cbi.submit") then
377 Node.parse(self, 1, ...)
381 for k, j in ipairs(self.children) do
382 for i, v in ipairs(j.children) do
384 and (not v.tag_missing or not v.tag_missing[1])
385 and (not v.tag_invalid or not v.tag_invalid[1])
390 not luci.http.formvalue("cbi.submit") and 0
394 self.dorender = not self.handle or self:handle(state, self.data) ~= false
397 function SimpleForm.render(self, ...)
398 if self.dorender then
399 Node.render(self, ...)
403 function SimpleForm.section(self, class, ...)
404 if instanceof(class, AbstractSection) then
405 local obj = class(self, ...)
409 error("class must be a descendent of AbstractSection")
413 -- Creates a child field
414 function SimpleForm.field(self, class, ...)
416 for k, v in ipairs(self.children) do
417 if instanceof(v, SimpleSection) then
423 section = self:section(SimpleSection)
426 if instanceof(class, AbstractValue) then
427 local obj = class(self, section, ...)
428 obj.track_missing = true
432 error("class must be a descendent of AbstractValue")
436 function SimpleForm.set(self, section, option, value)
437 self.data[option] = value
441 function SimpleForm.del(self, section, option)
442 self.data[option] = nil
446 function SimpleForm.get(self, section, option)
447 return self.data[option]
451 function SimpleForm.get_scheme()
460 AbstractSection = class(Node)
462 function AbstractSection.__init__(self, map, sectiontype, ...)
463 Node.__init__(self, ...)
464 self.sectiontype = sectiontype
466 self.config = map.config
471 self.tag_invalid = {}
472 self.tag_deperror = {}
475 self.addremove = false
479 -- Appends a new option
480 function AbstractSection.option(self, class, option, ...)
481 -- Autodetect from UVL
482 if class == true and self.map:get_scheme(self.sectiontype, option) then
483 local vs = self.map:get_scheme(self.sectiontype, option)
484 if vs.type == "boolean" then
486 elseif vs.type == "list" then
488 elseif vs.type == "enum" or vs.type == "reference" then
495 if instanceof(class, AbstractValue) then
496 local obj = class(self.map, self, option, ...)
498 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
501 self.fields[option] = obj
503 elseif class == true then
504 error("No valid class was given and autodetection failed.")
506 error("class must be a descendant of AbstractValue")
510 -- Parse optional options
511 function AbstractSection.parse_optionals(self, section)
512 if not self.optional then
516 self.optionals[section] = {}
518 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
519 for k,v in ipairs(self.children) do
520 if v.optional and not v:cfgvalue(section) then
521 if field == v.option then
524 table.insert(self.optionals[section], v)
529 if field and #field > 0 and self.dynamic then
530 self:add_dynamic(field)
534 -- Add a dynamic option
535 function AbstractSection.add_dynamic(self, field, optional)
536 local o = self:option(Value, field, field)
537 o.optional = optional
540 -- Parse all dynamic options
541 function AbstractSection.parse_dynamic(self, section)
542 if not self.dynamic then
546 local arr = luci.util.clone(self:cfgvalue(section))
547 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
548 for k, v in pairs(form) do
552 for key,val in pairs(arr) do
555 for i,c in ipairs(self.children) do
556 if c.option == key then
561 if create and key:sub(1, 1) ~= "." then
562 self:add_dynamic(key, true)
567 -- Returns the section's UCI table
568 function AbstractSection.cfgvalue(self, section)
569 return self.map:get(section)
572 -- Removes the section
573 function AbstractSection.remove(self, section)
574 return self.map:del(section)
577 -- Creates the section
578 function AbstractSection.create(self, section)
582 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
584 section = self.map:add(self.sectiontype)
589 for k,v in pairs(self.children) do
591 self.map:set(section, v.option, v.default)
595 for k,v in pairs(self.defaults) do
596 self.map:set(section, k, v)
604 SimpleSection = class(AbstractSection)
606 function SimpleSection.__init__(self, form, ...)
607 AbstractSection.__init__(self, form, nil, ...)
608 self.template = "cbi/nullsection"
612 Table = class(AbstractSection)
614 function Table.__init__(self, form, data, ...)
615 local datasource = {}
616 datasource.config = "table"
619 function datasource.get(self, section, option)
620 return data[section] and data[section][option]
623 function datasource.del(...)
627 function datasource.get_scheme()
631 AbstractSection.__init__(self, datasource, "table", ...)
632 self.template = "cbi/tblsection"
633 self.rowcolors = true
634 self.anonymous = true
637 function Table.parse(self)
638 for i, k in ipairs(self:cfgsections()) do
639 if luci.http.formvalue("cbi.submit") then
645 function Table.cfgsections(self)
648 for i, v in luci.util.kspairs(self.data) do
649 table.insert(sections, i)
658 NamedSection - A fixed configuration section defined by its name
660 NamedSection = class(AbstractSection)
662 function NamedSection.__init__(self, map, section, stype, ...)
663 AbstractSection.__init__(self, map, stype, ...)
664 Node._i18n(self, map.config, section, nil, ...)
667 self.addremove = false
669 -- Use defaults from UVL
670 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
671 local vs = self.map:get_scheme(self.sectiontype)
672 self.addremove = not vs.unique and not vs.required
673 self.dynamic = vs.dynamic
674 self.title = self.title or vs.title
675 self.description = self.description or vs.descr
678 self.template = "cbi/nsection"
679 self.section = section
682 function NamedSection.parse(self, novld)
683 local s = self.section
684 local active = self:cfgvalue(s)
686 if self.addremove then
687 local path = self.config.."."..s
688 if active then -- Remove the section
689 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
692 else -- Create and apply default values
693 if luci.http.formvalue("cbi.cns."..path) then
701 AbstractSection.parse_dynamic(self, s)
702 if luci.http.formvalue("cbi.submit") then
705 if not novld and not self.override_scheme and self.map.scheme then
706 _uvl_validate_section(self, s)
709 AbstractSection.parse_optionals(self, s)
715 TypedSection - A (set of) configuration section(s) defined by the type
716 addremove: Defines whether the user can add/remove sections of this type
717 anonymous: Allow creating anonymous sections
718 validate: a validation function returning nil if the section is invalid
720 TypedSection = class(AbstractSection)
722 function TypedSection.__init__(self, map, type, ...)
723 AbstractSection.__init__(self, map, type, ...)
724 Node._i18n(self, map.config, type, nil, ...)
726 self.template = "cbi/tsection"
728 self.anonymous = false
730 -- Use defaults from UVL
731 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
732 local vs = self.map:get_scheme(self.sectiontype)
733 self.addremove = not vs.unique and not vs.required
734 self.dynamic = vs.dynamic
735 self.anonymous = not vs.named
736 self.title = self.title or vs.title
737 self.description = self.description or vs.descr
741 -- Return all matching UCI sections for this TypedSection
742 function TypedSection.cfgsections(self)
744 self.map.uci:foreach(self.map.config, self.sectiontype,
746 if self:checkscope(section[".name"]) then
747 table.insert(sections, section[".name"])
754 -- Limits scope to sections that have certain option => value pairs
755 function TypedSection.depends(self, option, value)
756 table.insert(self.deps, {option=option, value=value})
759 function TypedSection.parse(self, novld)
760 if self.addremove then
762 local crval = REMOVE_PREFIX .. self.config
763 local name = luci.http.formvaluetable(crval)
764 for k,v in pairs(name) do
765 if k:sub(-2) == ".x" then
768 if self:cfgvalue(k) and self:checkscope(k) then
775 for i, k in ipairs(self:cfgsections()) do
776 AbstractSection.parse_dynamic(self, k)
777 if luci.http.formvalue("cbi.submit") then
780 if not novld and not self.override_scheme and self.map.scheme then
781 _uvl_validate_section(self, k)
784 AbstractSection.parse_optionals(self, k)
787 if self.addremove then
790 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
791 local name = luci.http.formvalue(crval)
792 if self.anonymous then
794 created = self:create()
798 -- Ignore if it already exists
799 if self:cfgvalue(name) then
803 name = self:checkscope(name)
806 self.err_invalid = true
809 if name and #name > 0 then
810 created = self:create(name) and name
812 self.invalid_cts = true
819 AbstractSection.parse_optionals(self, created)
824 -- Verifies scope of sections
825 function TypedSection.checkscope(self, section)
826 -- Check if we are not excluded
827 if self.filter and not self:filter(section) then
831 -- Check if at least one dependency is met
832 if #self.deps > 0 and self:cfgvalue(section) then
835 for k, v in ipairs(self.deps) do
836 if self:cfgvalue(section)[v.option] == v.value then
846 return self:validate(section)
850 -- Dummy validate function
851 function TypedSection.validate(self, section)
857 AbstractValue - An abstract Value Type
858 null: Value can be empty
859 valid: A function returning the value if it is valid otherwise nil
860 depends: A table of option => value pairs of which one must be true
861 default: The default value
862 size: The size of the input fields
863 rmempty: Unset value if empty
864 optional: This value is optional (see AbstractSection.optionals)
866 AbstractValue = class(Node)
868 function AbstractValue.__init__(self, map, section, option, ...)
869 Node.__init__(self, ...)
870 self.section = section
873 self.config = map.config
874 self.tag_invalid = {}
875 self.tag_missing = {}
876 self.tag_reqerror = {}
879 --self.cast = "string"
881 self.track_missing = false
882 --self.rmempty = false
885 self.optional = false
888 function AbstractValue.prepare(self)
889 -- Use defaults from UVL
890 if not self.override_scheme
891 and self.map:get_scheme(self.section.sectiontype, self.option) then
892 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
893 if self.rmempty == nil then
894 self.rmempty = not vs.required
896 if self.cast == nil then
897 self.cast = (vs.type == "list") and "list" or "string"
899 self.title = self.title or vs.title
900 self.description = self.description or vs.descr
901 if self.default == nil then
902 self.default = vs.default
905 if vs.depends and not self.override_dependencies then
906 for i, deps in ipairs(vs.depends) do
907 deps = _uvl_strip_remote_dependencies(deps)
915 self.cast = self.cast or "string"
918 -- Add a dependencie to another section field
919 function AbstractValue.depends(self, field, value)
921 if type(field) == "string" then
928 table.insert(self.deps, {deps=deps, add=""})
931 -- Generates the unique CBID
932 function AbstractValue.cbid(self, section)
933 return "cbid."..self.map.config.."."..section.."."..self.option
936 -- Return whether this object should be created
937 function AbstractValue.formcreated(self, section)
938 local key = "cbi.opt."..self.config.."."..section
939 return (luci.http.formvalue(key) == self.option)
942 -- Returns the formvalue for this object
943 function AbstractValue.formvalue(self, section)
944 return luci.http.formvalue(self:cbid(section))
947 function AbstractValue.additional(self, value)
948 self.optional = value
951 function AbstractValue.mandatory(self, value)
952 self.rmempty = not value
955 function AbstractValue.parse(self, section)
956 local fvalue = self:formvalue(section)
957 local cvalue = self:cfgvalue(section)
959 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
960 fvalue = self:transform(self:validate(fvalue, section))
962 self.tag_invalid[section] = true
964 if fvalue and not (fvalue == cvalue) then
965 self:write(section, fvalue)
967 else -- Unset the UCI or error
968 if self.rmempty or self.optional then
970 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
971 self.tag_missing[section] = true
976 -- Render if this value exists or if it is mandatory
977 function AbstractValue.render(self, s, scope)
978 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
981 scope.cbid = self:cbid(s)
982 scope.striptags = luci.util.striptags
984 scope.ifattr = function(cond,key,val)
986 return string.format(
987 ' %s="%s"', tostring(key),
988 luci.util.pcdata(tostring( val
990 or (type(self[key]) ~= "function" and self[key])
998 scope.attr = function(...)
999 return scope.ifattr( true, ... )
1002 Node.render(self, scope)
1006 -- Return the UCI value of this object
1007 function AbstractValue.cfgvalue(self, section)
1008 local value = self.map:get(section, self.option)
1011 elseif not self.cast or self.cast == type(value) then
1013 elseif self.cast == "string" then
1014 if type(value) == "table" then
1017 elseif self.cast == "table" then
1018 return luci.util.split(value, "%s+", nil, true)
1022 -- Validate the form value
1023 function AbstractValue.validate(self, value)
1027 AbstractValue.transform = AbstractValue.validate
1031 function AbstractValue.write(self, section, value)
1032 return self.map:set(section, self.option, value)
1036 function AbstractValue.remove(self, section)
1037 return self.map:del(section, self.option)
1044 Value - A one-line value
1045 maxlength: The maximum length
1047 Value = class(AbstractValue)
1049 function Value.__init__(self, ...)
1050 AbstractValue.__init__(self, ...)
1051 self.template = "cbi/value"
1056 function Value.value(self, key, val)
1058 table.insert(self.keylist, tostring(key))
1059 table.insert(self.vallist, tostring(val))
1063 -- DummyValue - This does nothing except being there
1064 DummyValue = class(AbstractValue)
1066 function DummyValue.__init__(self, ...)
1067 AbstractValue.__init__(self, ...)
1068 self.template = "cbi/dvalue"
1072 function DummyValue.cfgvalue(self, section)
1075 if type(self.value) == "function" then
1076 value = self:value(section)
1081 value = AbstractValue.cfgvalue(self, section)
1086 function DummyValue.parse(self)
1092 Flag - A flag being enabled or disabled
1094 Flag = class(AbstractValue)
1096 function Flag.__init__(self, ...)
1097 AbstractValue.__init__(self, ...)
1098 self.template = "cbi/fvalue"
1104 -- A flag can only have two states: set or unset
1105 function Flag.parse(self, section)
1106 local fvalue = self:formvalue(section)
1109 fvalue = self.enabled
1111 fvalue = self.disabled
1114 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1115 if not(fvalue == self:cfgvalue(section)) then
1116 self:write(section, fvalue)
1119 self:remove(section)
1126 ListValue - A one-line value predefined in a list
1127 widget: The widget that will be used (select, radio)
1129 ListValue = class(AbstractValue)
1131 function ListValue.__init__(self, ...)
1132 AbstractValue.__init__(self, ...)
1133 self.template = "cbi/lvalue"
1138 self.widget = "select"
1141 function ListValue.prepare(self, ...)
1142 AbstractValue.prepare(self, ...)
1143 if not self.override_scheme
1144 and self.map:get_scheme(self.section.sectiontype, self.option) then
1145 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1146 if self.value and vs.valuelist and not self.override_values then
1147 for k, v in ipairs(vs.valuelist) do
1149 if not self.override_dependencies
1150 and vs.enum_depends and vs.enum_depends[v.value] then
1151 for i, dep in ipairs(vs.enum_depends[v.value]) do
1152 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1155 self:value(v.value, v.title or v.value, unpack(deps))
1161 function ListValue.value(self, key, val, ...)
1162 if luci.util.contains(self.keylist, key) then
1167 table.insert(self.keylist, tostring(key))
1168 table.insert(self.vallist, tostring(val))
1170 for i, deps in ipairs({...}) do
1171 table.insert(self.deps, {add = "-"..key, deps=deps})
1175 function ListValue.validate(self, val)
1176 if luci.util.contains(self.keylist, val) then
1186 MultiValue - Multiple delimited values
1187 widget: The widget that will be used (select, checkbox)
1188 delimiter: The delimiter that will separate the values (default: " ")
1190 MultiValue = class(AbstractValue)
1192 function MultiValue.__init__(self, ...)
1193 AbstractValue.__init__(self, ...)
1194 self.template = "cbi/mvalue"
1199 self.widget = "checkbox"
1200 self.delimiter = " "
1203 function MultiValue.render(self, ...)
1204 if self.widget == "select" and not self.size then
1205 self.size = #self.vallist
1208 AbstractValue.render(self, ...)
1211 function MultiValue.value(self, key, val)
1212 if luci.util.contains(self.keylist, key) then
1217 table.insert(self.keylist, tostring(key))
1218 table.insert(self.vallist, tostring(val))
1221 function MultiValue.valuelist(self, section)
1222 local val = self:cfgvalue(section)
1224 if not(type(val) == "string") then
1228 return luci.util.split(val, self.delimiter)
1231 function MultiValue.validate(self, val)
1232 val = (type(val) == "table") and val or {val}
1236 for i, value in ipairs(val) do
1237 if luci.util.contains(self.keylist, value) then
1238 result = result and (result .. self.delimiter .. value) or value
1246 StaticList = class(MultiValue)
1248 function StaticList.__init__(self, ...)
1249 MultiValue.__init__(self, ...)
1251 self.valuelist = self.cfgvalue
1253 if not self.override_scheme
1254 and self.map:get_scheme(self.section.sectiontype, self.option) then
1255 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1256 if self.value and vs.values and not self.override_values then
1257 for k, v in pairs(vs.values) do
1264 function StaticList.validate(self, value)
1265 value = (type(value) == "table") and value or {value}
1268 for i, v in ipairs(value) do
1269 if luci.util.contains(self.vallist, v) then
1270 table.insert(valid, v)
1277 DynamicList = class(AbstractValue)
1279 function DynamicList.__init__(self, ...)
1280 AbstractValue.__init__(self, ...)
1281 self.template = "cbi/dynlist"
1287 function DynamicList.value(self, key, val)
1289 table.insert(self.keylist, tostring(key))
1290 table.insert(self.vallist, tostring(val))
1293 function DynamicList.formvalue(self, section)
1294 local value = AbstractValue.formvalue(self, section)
1295 value = (type(value) == "table") and value or {value}
1298 for i, v in ipairs(value) do
1300 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1301 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1302 table.insert(valid, v)
1311 TextValue - A multi-line value
1314 TextValue = class(AbstractValue)
1316 function TextValue.__init__(self, ...)
1317 AbstractValue.__init__(self, ...)
1318 self.template = "cbi/tvalue"
1324 Button = class(AbstractValue)
1326 function Button.__init__(self, ...)
1327 AbstractValue.__init__(self, ...)
1328 self.template = "cbi/button"
1329 self.inputstyle = nil
1334 FileUpload = class(AbstractValue)
1336 function FileUpload.__init__(self, ...)
1337 AbstractValue.__init__(self, ...)
1338 self.template = "cbi/upload"
1339 if not self.map.upload_fields then
1340 self.map.upload_fields = { self }
1342 self.map.upload_fields[#self.map.upload_fields+1] = self
1346 function FileUpload.cfgvalue(self, section)
1347 local val = AbstractValue.cfgvalue(self, section)
1348 if val and luci.fs.access(val) then
1354 function FileUpload.formvalue(self, section)
1355 local val = AbstractValue.formvalue(self, section)
1357 if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
1358 not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1368 function FileUpload.remove(self, section)
1369 local val = AbstractValue.formvalue(self, section)
1370 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1371 return AbstractValue.remove(self, section)