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")
34 local uci = require("luci.model.uci")
35 local class = luci.util.class
36 local instanceof = luci.util.instanceof
44 CREATE_PREFIX = "cbi.cts."
45 REMOVE_PREFIX = "cbi.rts."
47 -- Loads a CBI map from given file, creating an environment and returns it
48 function load(cbimap, ...)
50 local i18n = require "luci.i18n"
51 require("luci.config")
54 local cbidir = luci.util.libpath() .. "/model/cbi/"
55 local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
58 luci.i18n.loadc("cbi")
59 luci.i18n.loadc("uvl")
62 translate=i18n.translate,
63 translatef=i18n.translatef,
67 setfenv(func, setmetatable(env, {__index =
69 return rawget(tbl, key) or _M[key] or _G[key]
74 for i, map in ipairs(maps) do
75 if not instanceof(map, Node) then
76 error("CBI map returns no valid map object!")
84 local function _uvl_validate_section(node, name)
85 local co = node.map:get()
87 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
88 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
90 local function tag_fields(e)
91 if e.option and node.fields[e.option] then
92 if node.fields[e.option].error then
93 node.fields[e.option].error[name] = e
95 node.fields[e.option].error = { [name] = e }
98 for _, c in ipairs(e.childs) do tag_fields(c) end
102 local function tag_section(e)
104 for _, c in ipairs(e.childs) do
105 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
106 table.insert( s, c.childs[1]:string() )
108 table.insert( s, c:string() )
115 node.error = { [name] = s }
120 local stat, err = node.map.validator:validate_section(node.config, name, co)
122 node.map.save = false
129 local function _uvl_strip_remote_dependencies(deps)
132 for k, v in pairs(deps) do
133 k = k:gsub("%$config%.%$section%.", "")
134 if k:match("^[%w_]+$") and type(v) == "string" then
143 -- Node pseudo abstract class
146 function Node.__init__(self, title, description)
148 self.title = title or ""
149 self.description = description or ""
150 self.template = "cbi/node"
154 function Node._i18n(self, config, section, option, title, description)
157 if type(luci.i18n) == "table" then
159 local key = config and config:gsub("[^%w]+", "") or ""
161 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
162 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
164 self.title = title or luci.i18n.translate( key, option or section or config )
165 self.description = description or luci.i18n.translate( key .. "_desc", "" )
169 -- Append child nodes
170 function Node.append(self, obj)
171 table.insert(self.children, obj)
174 -- Parse this node and its children
175 function Node.parse(self, ...)
176 for k, child in ipairs(self.children) do
182 function Node.render(self, scope)
186 luci.template.render(self.template, scope)
189 -- Render the children
190 function Node.render_children(self, ...)
191 for k, node in ipairs(self.children) do
198 A simple template element
200 Template = class(Node)
202 function Template.__init__(self, template)
204 self.template = template
207 function Template.render(self)
208 luci.template.render(self.template, {self=self})
213 Map - A map describing a configuration file
217 function Map.__init__(self, config, ...)
218 Node.__init__(self, ...)
219 Node._i18n(self, config, nil, nil, ...)
222 self.parsechain = {self.config}
223 self.template = "cbi/map"
224 self.apply_on_parse = nil
225 self.uci = uci.cursor()
227 if not self.uci:load(self.config) then
228 error("Unable to read UCI data: " .. self.config)
231 self.validator = luci.uvl.UVL()
232 self.scheme = self.validator:get_scheme(self.config)
236 function Map.get_scheme(self, sectiontype, option)
238 return self.scheme and self.scheme.sections[sectiontype]
240 return self.scheme and self.scheme.variables[sectiontype]
241 and self.scheme.variables[sectiontype][option]
246 -- Chain foreign config
247 function Map.chain(self, config)
248 table.insert(self.parsechain, config)
251 -- Use optimized UCI writing
252 function Map.parse(self)
256 for i, config in ipairs(self.parsechain) do
257 self.uci:save(config)
259 if luci.http.formvalue("cbi.apply") then
260 for i, config in ipairs(self.parsechain) do
261 self.uci:commit(config)
263 -- Refresh data because commit changes section names
264 self.uci:load(config)
266 if self.apply_on_parse then
267 self.uci:apply(self.parsechain)
269 self._apply = function()
270 local cmd = self.uci:apply(self.parsechain, true)
276 Node.parse(self, true)
279 for i, config in ipairs(self.parsechain) do
280 self.uci:unload(config)
285 function Map.render(self, ...)
286 Node.render(self, ...)
288 local fp = self._apply()
294 -- Creates a child section
295 function Map.section(self, class, ...)
296 if instanceof(class, AbstractSection) then
297 local obj = class(self, ...)
301 error("class must be a descendent of AbstractSection")
306 function Map.add(self, sectiontype)
307 return self.uci:add(self.config, sectiontype)
311 function Map.set(self, section, option, value)
313 return self.uci:set(self.config, section, option, value)
315 return self.uci:set(self.config, section, value)
320 function Map.del(self, section, option)
322 return self.uci:delete(self.config, section, option)
324 return self.uci:delete(self.config, section)
329 function Map.get(self, section, option)
331 return self.uci:get_all(self.config)
333 return self.uci:get(self.config, section, option)
335 return self.uci:get_all(self.config, section)
345 Page.__init__ = Node.__init__
346 Page.parse = function() end
350 SimpleForm - A Simple non-UCI form
352 SimpleForm = class(Node)
354 function SimpleForm.__init__(self, config, title, description, data)
355 Node.__init__(self, title, description)
357 self.data = data or {}
358 self.template = "cbi/simpleform"
362 function SimpleForm.parse(self, ...)
363 if luci.http.formvalue("cbi.submit") then
364 Node.parse(self, 1, ...)
368 for k, j in ipairs(self.children) do
369 for i, v in ipairs(j.children) do
371 and (not v.tag_missing or not v.tag_missing[1])
372 and (not v.tag_invalid or not v.tag_invalid[1])
377 not luci.http.formvalue("cbi.submit") and 0
381 self.dorender = not self.handle or self:handle(state, self.data) ~= false
384 function SimpleForm.render(self, ...)
385 if self.dorender then
386 Node.render(self, ...)
390 function SimpleForm.section(self, class, ...)
391 if instanceof(class, AbstractSection) then
392 local obj = class(self, ...)
396 error("class must be a descendent of AbstractSection")
400 -- Creates a child field
401 function SimpleForm.field(self, class, ...)
403 for k, v in ipairs(self.children) do
404 if instanceof(v, SimpleSection) then
410 section = self:section(SimpleSection)
413 if instanceof(class, AbstractValue) then
414 local obj = class(self, section, ...)
415 obj.track_missing = true
419 error("class must be a descendent of AbstractValue")
423 function SimpleForm.set(self, section, option, value)
424 self.data[option] = value
428 function SimpleForm.del(self, section, option)
429 self.data[option] = nil
433 function SimpleForm.get(self, section, option)
434 return self.data[option]
438 function SimpleForm.get_scheme()
447 AbstractSection = class(Node)
449 function AbstractSection.__init__(self, map, sectiontype, ...)
450 Node.__init__(self, ...)
451 self.sectiontype = sectiontype
453 self.config = map.config
458 self.tag_invalid = {}
459 self.tag_deperror = {}
462 self.addremove = false
466 -- Appends a new option
467 function AbstractSection.option(self, class, option, ...)
468 -- Autodetect from UVL
469 if class == true and self.map:get_scheme(self.sectiontype, option) then
470 local vs = self.map:get_scheme(self.sectiontype, option)
471 if vs.type == "boolean" then
473 elseif vs.type == "list" then
475 elseif vs.type == "enum" or vs.type == "reference" then
482 if instanceof(class, AbstractValue) then
483 local obj = class(self.map, self, option, ...)
485 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
488 self.fields[option] = obj
490 elseif class == true then
491 error("No valid class was given and autodetection failed.")
493 error("class must be a descendant of AbstractValue")
497 -- Parse optional options
498 function AbstractSection.parse_optionals(self, section)
499 if not self.optional then
503 self.optionals[section] = {}
505 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
506 for k,v in ipairs(self.children) do
507 if v.optional and not v:cfgvalue(section) then
508 if field == v.option then
511 table.insert(self.optionals[section], v)
516 if field and #field > 0 and self.dynamic then
517 self:add_dynamic(field)
521 -- Add a dynamic option
522 function AbstractSection.add_dynamic(self, field, optional)
523 local o = self:option(Value, field, field)
524 o.optional = optional
527 -- Parse all dynamic options
528 function AbstractSection.parse_dynamic(self, section)
529 if not self.dynamic then
533 local arr = luci.util.clone(self:cfgvalue(section))
534 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
535 for k, v in pairs(form) do
539 for key,val in pairs(arr) do
542 for i,c in ipairs(self.children) do
543 if c.option == key then
548 if create and key:sub(1, 1) ~= "." then
549 self:add_dynamic(key, true)
554 -- Returns the section's UCI table
555 function AbstractSection.cfgvalue(self, section)
556 return self.map:get(section)
559 -- Removes the section
560 function AbstractSection.remove(self, section)
561 return self.map:del(section)
564 -- Creates the section
565 function AbstractSection.create(self, section)
569 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
571 section = self.map:add(self.sectiontype)
576 for k,v in pairs(self.children) do
578 self.map:set(section, v.option, v.default)
582 for k,v in pairs(self.defaults) do
583 self.map:set(section, k, v)
591 SimpleSection = class(AbstractSection)
593 function SimpleSection.__init__(self, form, ...)
594 AbstractSection.__init__(self, form, nil, ...)
595 self.template = "cbi/nullsection"
599 Table = class(AbstractSection)
601 function Table.__init__(self, form, data, ...)
602 local datasource = {}
603 datasource.config = "table"
606 function datasource.get(self, section, option)
607 return data[section] and data[section][option]
610 function datasource.del(...)
614 function datasource.get_scheme()
618 AbstractSection.__init__(self, datasource, "table", ...)
619 self.template = "cbi/tblsection"
620 self.rowcolors = true
621 self.anonymous = true
624 function Table.parse(self)
625 for i, k in ipairs(self:cfgsections()) do
626 if luci.http.formvalue("cbi.submit") then
632 function Table.cfgsections(self)
635 for i, v in luci.util.kspairs(self.data) do
636 table.insert(sections, i)
645 NamedSection - A fixed configuration section defined by its name
647 NamedSection = class(AbstractSection)
649 function NamedSection.__init__(self, map, section, stype, ...)
650 AbstractSection.__init__(self, map, stype, ...)
651 Node._i18n(self, map.config, section, nil, ...)
654 self.addremove = false
656 -- Use defaults from UVL
657 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
658 local vs = self.map:get_scheme(self.sectiontype)
659 self.addremove = not vs.unique and not vs.required
660 self.dynamic = vs.dynamic
661 self.title = self.title or vs.title
662 self.description = self.description or vs.descr
665 self.template = "cbi/nsection"
666 self.section = section
669 function NamedSection.parse(self, novld)
670 local s = self.section
671 local active = self:cfgvalue(s)
673 if self.addremove then
674 local path = self.config.."."..s
675 if active then -- Remove the section
676 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
679 else -- Create and apply default values
680 if luci.http.formvalue("cbi.cns."..path) then
688 AbstractSection.parse_dynamic(self, s)
689 if luci.http.formvalue("cbi.submit") then
692 if not novld and not self.override_scheme and self.map.scheme then
693 _uvl_validate_section(self, s)
696 AbstractSection.parse_optionals(self, s)
702 TypedSection - A (set of) configuration section(s) defined by the type
703 addremove: Defines whether the user can add/remove sections of this type
704 anonymous: Allow creating anonymous sections
705 validate: a validation function returning nil if the section is invalid
707 TypedSection = class(AbstractSection)
709 function TypedSection.__init__(self, map, type, ...)
710 AbstractSection.__init__(self, map, type, ...)
711 Node._i18n(self, map.config, type, nil, ...)
713 self.template = "cbi/tsection"
715 self.anonymous = false
717 -- Use defaults from UVL
718 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
719 local vs = self.map:get_scheme(self.sectiontype)
720 self.addremove = not vs.unique and not vs.required
721 self.dynamic = vs.dynamic
722 self.anonymous = not vs.named
723 self.title = self.title or vs.title
724 self.description = self.description or vs.descr
728 -- Return all matching UCI sections for this TypedSection
729 function TypedSection.cfgsections(self)
731 self.map.uci:foreach(self.map.config, self.sectiontype,
733 if self:checkscope(section[".name"]) then
734 table.insert(sections, section[".name"])
741 -- Limits scope to sections that have certain option => value pairs
742 function TypedSection.depends(self, option, value)
743 table.insert(self.deps, {option=option, value=value})
746 function TypedSection.parse(self, novld)
747 if self.addremove then
749 local crval = REMOVE_PREFIX .. self.config
750 local name = luci.http.formvaluetable(crval)
751 for k,v in pairs(name) do
752 if k:sub(-2) == ".x" then
755 if self:cfgvalue(k) and self:checkscope(k) then
762 for i, k in ipairs(self:cfgsections()) do
763 AbstractSection.parse_dynamic(self, k)
764 if luci.http.formvalue("cbi.submit") then
767 if not novld and not self.override_scheme and self.map.scheme then
768 _uvl_validate_section(self, k)
771 AbstractSection.parse_optionals(self, k)
774 if self.addremove then
777 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
778 local name = luci.http.formvalue(crval)
779 if self.anonymous then
781 created = self:create()
785 -- Ignore if it already exists
786 if self:cfgvalue(name) then
790 name = self:checkscope(name)
793 self.err_invalid = true
796 if name and #name > 0 then
797 created = self:create(name) and name
799 self.invalid_cts = true
806 AbstractSection.parse_optionals(self, created)
811 -- Verifies scope of sections
812 function TypedSection.checkscope(self, section)
813 -- Check if we are not excluded
814 if self.filter and not self:filter(section) then
818 -- Check if at least one dependency is met
819 if #self.deps > 0 and self:cfgvalue(section) then
822 for k, v in ipairs(self.deps) do
823 if self:cfgvalue(section)[v.option] == v.value then
833 return self:validate(section)
837 -- Dummy validate function
838 function TypedSection.validate(self, section)
844 AbstractValue - An abstract Value Type
845 null: Value can be empty
846 valid: A function returning the value if it is valid otherwise nil
847 depends: A table of option => value pairs of which one must be true
848 default: The default value
849 size: The size of the input fields
850 rmempty: Unset value if empty
851 optional: This value is optional (see AbstractSection.optionals)
853 AbstractValue = class(Node)
855 function AbstractValue.__init__(self, map, section, option, ...)
856 Node.__init__(self, ...)
857 self.section = section
860 self.config = map.config
861 self.tag_invalid = {}
862 self.tag_missing = {}
863 self.tag_reqerror = {}
868 self.track_missing = false
872 self.optional = false
874 -- Use defaults from UVL
875 if not self.override_scheme
876 and self.map:get_scheme(self.section.sectiontype, self.option) then
877 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
878 self.rmempty = not vs.required
879 self.cast = (vs.type == "list") and "list" or "string"
880 self.title = self.title or vs.title
881 self.description = self.description or vs.descr
882 self.default = vs.default
884 if vs.depends and not self.override_dependencies then
885 for i, deps in ipairs(vs.depends) do
886 deps = _uvl_strip_remote_dependencies(deps)
895 -- Add a dependencie to another section field
896 function AbstractValue.depends(self, field, value)
898 if type(field) == "string" then
905 table.insert(self.deps, {deps=deps, add=""})
908 -- Generates the unique CBID
909 function AbstractValue.cbid(self, section)
910 return "cbid."..self.map.config.."."..section.."."..self.option
913 -- Return whether this object should be created
914 function AbstractValue.formcreated(self, section)
915 local key = "cbi.opt."..self.config.."."..section
916 return (luci.http.formvalue(key) == self.option)
919 -- Returns the formvalue for this object
920 function AbstractValue.formvalue(self, section)
921 return luci.http.formvalue(self:cbid(section))
924 function AbstractValue.additional(self, value)
925 self.optional = value
928 function AbstractValue.mandatory(self, value)
929 self.rmempty = not value
932 function AbstractValue.parse(self, section)
933 local fvalue = self:formvalue(section)
934 local cvalue = self:cfgvalue(section)
936 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
937 fvalue = self:transform(self:validate(fvalue, section))
939 self.tag_invalid[section] = true
941 if fvalue and not (fvalue == cvalue) then
942 self:write(section, fvalue)
944 else -- Unset the UCI or error
945 if self.rmempty or self.optional then
947 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
948 self.tag_missing[section] = true
953 -- Render if this value exists or if it is mandatory
954 function AbstractValue.render(self, s, scope)
955 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
958 scope.cbid = self:cbid(s)
959 scope.striptags = luci.util.striptags
961 scope.ifattr = function(cond,key,val)
963 return string.format(
964 ' %s="%s"', tostring(key),
965 luci.util.pcdata(tostring( val
967 or (type(self[key]) ~= "function" and self[key])
975 scope.attr = function(...)
976 return scope.ifattr( true, ... )
979 Node.render(self, scope)
983 -- Return the UCI value of this object
984 function AbstractValue.cfgvalue(self, section)
985 local value = self.map:get(section, self.option)
988 elseif not self.cast or self.cast == type(value) then
990 elseif self.cast == "string" then
991 if type(value) == "table" then
994 elseif self.cast == "table" then
999 -- Validate the form value
1000 function AbstractValue.validate(self, value)
1004 AbstractValue.transform = AbstractValue.validate
1008 function AbstractValue.write(self, section, value)
1009 return self.map:set(section, self.option, value)
1013 function AbstractValue.remove(self, section)
1014 return self.map:del(section, self.option)
1021 Value - A one-line value
1022 maxlength: The maximum length
1024 Value = class(AbstractValue)
1026 function Value.__init__(self, ...)
1027 AbstractValue.__init__(self, ...)
1028 self.template = "cbi/value"
1033 function Value.value(self, key, val)
1035 table.insert(self.keylist, tostring(key))
1036 table.insert(self.vallist, tostring(val))
1040 -- DummyValue - This does nothing except being there
1041 DummyValue = class(AbstractValue)
1043 function DummyValue.__init__(self, ...)
1044 AbstractValue.__init__(self, ...)
1045 self.template = "cbi/dvalue"
1049 function DummyValue.parse(self)
1055 Flag - A flag being enabled or disabled
1057 Flag = class(AbstractValue)
1059 function Flag.__init__(self, ...)
1060 AbstractValue.__init__(self, ...)
1061 self.template = "cbi/fvalue"
1067 -- A flag can only have two states: set or unset
1068 function Flag.parse(self, section)
1069 local fvalue = self:formvalue(section)
1072 fvalue = self.enabled
1074 fvalue = self.disabled
1077 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1078 if not(fvalue == self:cfgvalue(section)) then
1079 self:write(section, fvalue)
1082 self:remove(section)
1089 ListValue - A one-line value predefined in a list
1090 widget: The widget that will be used (select, radio)
1092 ListValue = class(AbstractValue)
1094 function ListValue.__init__(self, ...)
1095 AbstractValue.__init__(self, ...)
1096 self.template = "cbi/lvalue"
1101 self.widget = "select"
1103 if not self.override_scheme
1104 and self.map:get_scheme(self.section.sectiontype, self.option) then
1105 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1106 if self.value and vs.values and not self.override_values then
1107 if self.rmempty or self.optional then
1110 for k, v in pairs(vs.values) do
1112 if not self.override_dependencies
1113 and vs.enum_depends and vs.enum_depends[k] then
1114 for i, dep in ipairs(vs.enum_depends[k]) do
1115 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1118 self:value(k, v, unpack(deps))
1124 function ListValue.value(self, key, val, ...)
1125 if luci.util.contains(self.keylist, key) then
1130 table.insert(self.keylist, tostring(key))
1131 table.insert(self.vallist, tostring(val))
1133 for i, deps in ipairs({...}) do
1134 table.insert(self.deps, {add = "-"..key, deps=deps})
1138 function ListValue.validate(self, val)
1139 if luci.util.contains(self.keylist, val) then
1149 MultiValue - Multiple delimited values
1150 widget: The widget that will be used (select, checkbox)
1151 delimiter: The delimiter that will separate the values (default: " ")
1153 MultiValue = class(AbstractValue)
1155 function MultiValue.__init__(self, ...)
1156 AbstractValue.__init__(self, ...)
1157 self.template = "cbi/mvalue"
1162 self.widget = "checkbox"
1163 self.delimiter = " "
1166 function MultiValue.render(self, ...)
1167 if self.widget == "select" and not self.size then
1168 self.size = #self.vallist
1171 AbstractValue.render(self, ...)
1174 function MultiValue.value(self, key, val)
1175 if luci.util.contains(self.keylist, key) then
1180 table.insert(self.keylist, tostring(key))
1181 table.insert(self.vallist, tostring(val))
1184 function MultiValue.valuelist(self, section)
1185 local val = self:cfgvalue(section)
1187 if not(type(val) == "string") then
1191 return luci.util.split(val, self.delimiter)
1194 function MultiValue.validate(self, val)
1195 val = (type(val) == "table") and val or {val}
1199 for i, value in ipairs(val) do
1200 if luci.util.contains(self.keylist, value) then
1201 result = result and (result .. self.delimiter .. value) or value
1209 StaticList = class(MultiValue)
1211 function StaticList.__init__(self, ...)
1212 MultiValue.__init__(self, ...)
1214 self.valuelist = self.cfgvalue
1216 if not self.override_scheme
1217 and self.map:get_scheme(self.section.sectiontype, self.option) then
1218 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1219 if self.value and vs.values and not self.override_values then
1220 for k, v in pairs(vs.values) do
1227 function StaticList.validate(self, value)
1228 value = (type(value) == "table") and value or {value}
1231 for i, v in ipairs(value) do
1232 if luci.util.contains(self.vallist, v) then
1233 table.insert(valid, v)
1240 DynamicList = class(AbstractValue)
1242 function DynamicList.__init__(self, ...)
1243 AbstractValue.__init__(self, ...)
1244 self.template = "cbi/dynlist"
1250 function DynamicList.value(self, key, val)
1252 table.insert(self.keylist, tostring(key))
1253 table.insert(self.vallist, tostring(val))
1256 function DynamicList.formvalue(self, section)
1257 local value = AbstractValue.formvalue(self, section)
1258 value = (type(value) == "table") and value or {value}
1261 for i, v in ipairs(value) do
1263 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1264 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1265 table.insert(valid, v)
1274 TextValue - A multi-line value
1277 TextValue = class(AbstractValue)
1279 function TextValue.__init__(self, ...)
1280 AbstractValue.__init__(self, ...)
1281 self.template = "cbi/tvalue"
1287 Button = class(AbstractValue)
1289 function Button.__init__(self, ...)
1290 AbstractValue.__init__(self, ...)
1291 self.template = "cbi/button"
1292 self.inputstyle = nil