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 = 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
803 AbstractSection.parse_optionals(self, created)
808 -- Verifies scope of sections
809 function TypedSection.checkscope(self, section)
810 -- Check if we are not excluded
811 if self.filter and not self:filter(section) then
815 -- Check if at least one dependency is met
816 if #self.deps > 0 and self:cfgvalue(section) then
819 for k, v in ipairs(self.deps) do
820 if self:cfgvalue(section)[v.option] == v.value then
830 return self:validate(section)
834 -- Dummy validate function
835 function TypedSection.validate(self, section)
841 AbstractValue - An abstract Value Type
842 null: Value can be empty
843 valid: A function returning the value if it is valid otherwise nil
844 depends: A table of option => value pairs of which one must be true
845 default: The default value
846 size: The size of the input fields
847 rmempty: Unset value if empty
848 optional: This value is optional (see AbstractSection.optionals)
850 AbstractValue = class(Node)
852 function AbstractValue.__init__(self, map, section, option, ...)
853 Node.__init__(self, ...)
854 self.section = section
857 self.config = map.config
858 self.tag_invalid = {}
859 self.tag_missing = {}
860 self.tag_reqerror = {}
865 self.track_missing = false
869 self.optional = false
871 -- Use defaults from UVL
872 if not self.override_scheme
873 and self.map:get_scheme(self.section.sectiontype, self.option) then
874 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
875 self.rmempty = not vs.required
876 self.cast = (vs.type == "list") and "list" or "string"
877 self.title = self.title or vs.title
878 self.description = self.description or vs.descr
879 self.default = vs.default
881 if vs.depends and not self.override_dependencies then
882 for i, deps in ipairs(vs.depends) do
883 deps = _uvl_strip_remote_dependencies(deps)
892 -- Add a dependencie to another section field
893 function AbstractValue.depends(self, field, value)
895 if type(field) == "string" then
902 table.insert(self.deps, {deps=deps, add=""})
905 -- Generates the unique CBID
906 function AbstractValue.cbid(self, section)
907 return "cbid."..self.map.config.."."..section.."."..self.option
910 -- Return whether this object should be created
911 function AbstractValue.formcreated(self, section)
912 local key = "cbi.opt."..self.config.."."..section
913 return (luci.http.formvalue(key) == self.option)
916 -- Returns the formvalue for this object
917 function AbstractValue.formvalue(self, section)
918 return luci.http.formvalue(self:cbid(section))
921 function AbstractValue.additional(self, value)
922 self.optional = value
925 function AbstractValue.mandatory(self, value)
926 self.rmempty = not value
929 function AbstractValue.parse(self, section)
930 local fvalue = self:formvalue(section)
931 local cvalue = self:cfgvalue(section)
933 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
934 fvalue = self:transform(self:validate(fvalue, section))
936 self.tag_invalid[section] = true
938 if fvalue and not (fvalue == cvalue) then
939 self:write(section, fvalue)
941 else -- Unset the UCI or error
942 if self.rmempty or self.optional then
944 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
945 self.tag_missing[section] = true
950 -- Render if this value exists or if it is mandatory
951 function AbstractValue.render(self, s, scope)
952 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
955 scope.cbid = self:cbid(s)
956 scope.striptags = luci.util.striptags
958 scope.ifattr = function(cond,key,val)
960 return string.format(
961 ' %s="%s"', tostring(key),
962 luci.util.pcdata(tostring( val
964 or (type(self[key]) ~= "function" and self[key])
972 scope.attr = function(...)
973 return scope.ifattr( true, ... )
976 Node.render(self, scope)
980 -- Return the UCI value of this object
981 function AbstractValue.cfgvalue(self, section)
982 local value = self.map:get(section, self.option)
983 if not self.cast or self.cast == type(value) then
985 elseif self.cast == "string" then
986 if type(value) == "table" then
989 elseif self.cast == "table" then
994 -- Validate the form value
995 function AbstractValue.validate(self, value)
999 AbstractValue.transform = AbstractValue.validate
1003 function AbstractValue.write(self, section, value)
1004 return self.map:set(section, self.option, value)
1008 function AbstractValue.remove(self, section)
1009 return self.map:del(section, self.option)
1016 Value - A one-line value
1017 maxlength: The maximum length
1019 Value = class(AbstractValue)
1021 function Value.__init__(self, ...)
1022 AbstractValue.__init__(self, ...)
1023 self.template = "cbi/value"
1028 function Value.value(self, key, val)
1030 table.insert(self.keylist, tostring(key))
1031 table.insert(self.vallist, tostring(val))
1035 -- DummyValue - This does nothing except being there
1036 DummyValue = class(AbstractValue)
1038 function DummyValue.__init__(self, ...)
1039 AbstractValue.__init__(self, ...)
1040 self.template = "cbi/dvalue"
1044 function DummyValue.parse(self)
1050 Flag - A flag being enabled or disabled
1052 Flag = class(AbstractValue)
1054 function Flag.__init__(self, ...)
1055 AbstractValue.__init__(self, ...)
1056 self.template = "cbi/fvalue"
1062 -- A flag can only have two states: set or unset
1063 function Flag.parse(self, section)
1064 local fvalue = self:formvalue(section)
1067 fvalue = self.enabled
1069 fvalue = self.disabled
1072 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1073 if not(fvalue == self:cfgvalue(section)) then
1074 self:write(section, fvalue)
1077 self:remove(section)
1084 ListValue - A one-line value predefined in a list
1085 widget: The widget that will be used (select, radio)
1087 ListValue = class(AbstractValue)
1089 function ListValue.__init__(self, ...)
1090 AbstractValue.__init__(self, ...)
1091 self.template = "cbi/lvalue"
1096 self.widget = "select"
1098 if not self.override_scheme
1099 and self.map:get_scheme(self.section.sectiontype, self.option) then
1100 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1101 if self.value and vs.values and not self.override_values then
1102 if self.rmempty or self.optional then
1105 for k, v in pairs(vs.values) do
1107 if not self.override_dependencies
1108 and vs.enum_depends and vs.enum_depends[k] then
1109 for i, dep in ipairs(vs.enum_depends[k]) do
1110 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1113 self:value(k, v, unpack(deps))
1119 function ListValue.value(self, key, val, ...)
1120 if luci.util.contains(self.keylist, key) then
1125 table.insert(self.keylist, tostring(key))
1126 table.insert(self.vallist, tostring(val))
1128 for i, deps in ipairs({...}) do
1129 table.insert(self.deps, {add = "-"..key, deps=deps})
1133 function ListValue.validate(self, val)
1134 if luci.util.contains(self.keylist, val) then
1144 MultiValue - Multiple delimited values
1145 widget: The widget that will be used (select, checkbox)
1146 delimiter: The delimiter that will separate the values (default: " ")
1148 MultiValue = class(AbstractValue)
1150 function MultiValue.__init__(self, ...)
1151 AbstractValue.__init__(self, ...)
1152 self.template = "cbi/mvalue"
1157 self.widget = "checkbox"
1158 self.delimiter = " "
1161 function MultiValue.render(self, ...)
1162 if self.widget == "select" and not self.size then
1163 self.size = #self.vallist
1166 AbstractValue.render(self, ...)
1169 function MultiValue.value(self, key, val)
1170 if luci.util.contains(self.keylist, key) then
1175 table.insert(self.keylist, tostring(key))
1176 table.insert(self.vallist, tostring(val))
1179 function MultiValue.valuelist(self, section)
1180 local val = self:cfgvalue(section)
1182 if not(type(val) == "string") then
1186 return luci.util.split(val, self.delimiter)
1189 function MultiValue.validate(self, val)
1190 val = (type(val) == "table") and val or {val}
1194 for i, value in ipairs(val) do
1195 if luci.util.contains(self.keylist, value) then
1196 result = result and (result .. self.delimiter .. value) or value
1204 StaticList = class(MultiValue)
1206 function StaticList.__init__(self, ...)
1207 MultiValue.__init__(self, ...)
1209 self.valuelist = self.cfgvalue
1211 if not self.override_scheme
1212 and self.map:get_scheme(self.section.sectiontype, self.option) then
1213 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1214 if self.value and vs.values and not self.override_values then
1215 for k, v in pairs(vs.values) do
1222 function StaticList.validate(self, value)
1223 value = (type(value) == "table") and value or {value}
1226 for i, v in ipairs(value) do
1227 if luci.util.contains(self.valuelist, v) then
1228 table.insert(valid, v)
1235 DynamicList = class(AbstractValue)
1237 function DynamicList.__init__(self, ...)
1238 AbstractValue.__init__(self, ...)
1239 self.template = "cbi/dynlist"
1245 function DynamicList.value(self, key, val)
1247 table.insert(self.keylist, tostring(key))
1248 table.insert(self.vallist, tostring(val))
1251 function DynamicList.validate(self, value, section)
1252 value = (type(value) == "table") and value or {value}
1255 for i, v in ipairs(value) do
1257 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1258 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1259 table.insert(valid, v)
1268 TextValue - A multi-line value
1271 TextValue = class(AbstractValue)
1273 function TextValue.__init__(self, ...)
1274 AbstractValue.__init__(self, ...)
1275 self.template = "cbi/tvalue"
1281 Button = class(AbstractValue)
1283 function Button.__init__(self, ...)
1284 AbstractValue.__init__(self, ...)
1285 self.template = "cbi/button"
1286 self.inputstyle = nil