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, ...)
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")
60 luci.util.resfenv(func)
61 luci.util.updfenv(func, luci.cbi)
62 luci.util.extfenv(func, "translate", luci.i18n.translate)
63 luci.util.extfenv(func, "translatef", luci.i18n.translatef)
64 luci.util.extfenv(func, "arg", {...})
68 for i, map in ipairs(maps) do
69 if not instanceof(map, Node) then
70 error("CBI map returns no valid map object!")
78 local function _uvl_validate_section(node, name)
79 local co = node.map:get()
81 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
82 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
84 local stat, err = node.map.validator:validate_section(node.config, name, co)
87 if err:is(luci.uvl.errors.ERR_DEPENDENCY) then
88 node.tag_deperror[name] = true
90 node.tag_invalid[name] = true
92 for i, v in ipairs(err.childs) do
93 if v.option and node.fields[v.option] then
94 if v:is(luci.uvl.errors.ERR_DEPENDENCY) then
95 node.fields[v.option].tag_reqerror[name] = true
96 elseif v:is(luci.uvl.errors.ERR_OPT_REQUIRED) then
97 node.fields[v.option].tag_missing[name] = true
98 node.tag_deperror[name] = true
99 elseif v:is(luci.uvl.errors.ERR_OPTION) then
100 node.fields[v.option].tag_invalid[name] = true
108 local function _uvl_strip_remote_dependencies(deps)
111 for k, v in pairs(deps) do
112 k = k:gsub("%$config%.%$section%.", "")
113 if k:match("^[%w_]+$") and type(v) == "string" then
122 -- Node pseudo abstract class
125 function Node.__init__(self, title, description)
127 self.title = title or ""
128 self.description = description or ""
129 self.template = "cbi/node"
133 function Node._i18n(self, config, section, option, title, description)
136 if type(luci.i18n) == "table" then
138 local key = config and config:gsub("[^%w]+", "") or ""
140 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
141 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
143 self.title = title or luci.i18n.translate( key, option or section or config )
144 self.description = description or luci.i18n.translate( key .. "_desc", "" )
148 -- Append child nodes
149 function Node.append(self, obj)
150 table.insert(self.children, obj)
153 -- Parse this node and its children
154 function Node.parse(self, ...)
155 for k, child in ipairs(self.children) do
161 function Node.render(self, scope)
165 luci.template.render(self.template, scope)
168 -- Render the children
169 function Node.render_children(self, ...)
170 for k, node in ipairs(self.children) do
177 A simple template element
179 Template = class(Node)
181 function Template.__init__(self, template)
183 self.template = template
186 function Template.render(self)
187 luci.template.render(self.template, {self=self})
192 Map - A map describing a configuration file
196 function Map.__init__(self, config, ...)
197 Node.__init__(self, ...)
198 Node._i18n(self, config, nil, nil, ...)
201 self.parsechain = {self.config}
202 self.template = "cbi/map"
203 self.uci = uci.cursor()
205 if not self.uci:load(self.config) then
206 error("Unable to read UCI data: " .. self.config)
209 self.validator = luci.uvl.UVL()
210 self.scheme = self.validator:get_scheme(self.config)
214 function Map.get_scheme(self, sectiontype, option)
216 return self.scheme and self.scheme.sections[sectiontype]
218 return self.scheme and self.scheme.variables[sectiontype]
219 and self.scheme.variables[sectiontype][option]
224 -- Chain foreign config
225 function Map.chain(self, config)
226 table.insert(self.parsechain, config)
229 -- Use optimized UCI writing
230 function Map.parse(self, ...)
231 Node.parse(self, ...)
234 for i, config in ipairs(self.parsechain) do
235 self.uci:save(config)
237 if luci.http.formvalue("cbi.apply") then
238 for i, config in ipairs(self.parsechain) do
239 self.uci:commit(config)
240 self.uci:apply(config)
242 -- Refresh data because commit changes section names
243 self.uci:load(config)
247 Node.parse(self, ...)
250 for i, config in ipairs(self.parsechain) do
251 self.uci:unload(config)
256 -- Creates a child section
257 function Map.section(self, class, ...)
258 if instanceof(class, AbstractSection) then
259 local obj = class(self, ...)
263 error("class must be a descendent of AbstractSection")
268 function Map.add(self, sectiontype)
269 return self.uci:add(self.config, sectiontype)
273 function Map.set(self, section, option, value)
275 return self.uci:set(self.config, section, option, value)
277 return self.uci:set(self.config, section, value)
282 function Map.del(self, section, option)
284 return self.uci:delete(self.config, section, option)
286 return self.uci:delete(self.config, section)
291 function Map.get(self, section, option)
293 return self.uci:get_all(self.config)
295 return self.uci:get(self.config, section, option)
297 return self.uci:get_all(self.config, section)
307 Page.__init__ = Node.__init__
308 Page.parse = function() end
312 SimpleForm - A Simple non-UCI form
314 SimpleForm = class(Node)
316 function SimpleForm.__init__(self, config, title, description, data)
317 Node.__init__(self, title, description)
319 self.data = data or {}
320 self.template = "cbi/simpleform"
324 function SimpleForm.parse(self, ...)
325 if luci.http.formvalue("cbi.submit") then
326 Node.parse(self, 1, ...)
330 for k, j in ipairs(self.children) do
331 for i, v in ipairs(j.children) do
333 and (not v.tag_missing or not v.tag_missing[1])
334 and (not v.tag_invalid or not v.tag_invalid[1])
339 not luci.http.formvalue("cbi.submit") and 0
343 self.dorender = not self.handle or self:handle(state, self.data) ~= false
346 function SimpleForm.render(self, ...)
347 if self.dorender then
348 Node.render(self, ...)
352 function SimpleForm.section(self, class, ...)
353 if instanceof(class, AbstractSection) then
354 local obj = class(self, ...)
358 error("class must be a descendent of AbstractSection")
362 -- Creates a child field
363 function SimpleForm.field(self, class, ...)
365 for k, v in ipairs(self.children) do
366 if instanceof(v, SimpleSection) then
372 section = self:section(SimpleSection)
375 if instanceof(class, AbstractValue) then
376 local obj = class(self, section, ...)
377 obj.track_missing = true
381 error("class must be a descendent of AbstractValue")
385 function SimpleForm.set(self, section, option, value)
386 self.data[option] = value
390 function SimpleForm.del(self, section, option)
391 self.data[option] = nil
395 function SimpleForm.get(self, section, option)
396 return self.data[option]
400 function SimpleForm.get_scheme()
409 AbstractSection = class(Node)
411 function AbstractSection.__init__(self, map, sectiontype, ...)
412 Node.__init__(self, ...)
413 self.sectiontype = sectiontype
415 self.config = map.config
420 self.tag_invalid = {}
421 self.tag_deperror = {}
424 self.addremove = false
428 -- Appends a new option
429 function AbstractSection.option(self, class, option, ...)
430 -- Autodetect from UVL
431 if class == true and self.map:get_scheme(self.sectiontype, option) then
432 local vs = self.map:get_scheme(self.sectiontype, option)
433 if vs.type == "boolean" then
435 elseif vs.type == "list" then
437 elseif vs.type == "enum" or vs.type == "reference" then
444 if instanceof(class, AbstractValue) then
445 local obj = class(self.map, self, option, ...)
447 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
450 self.fields[option] = obj
452 elseif class == true then
453 error("No valid class was given and autodetection failed.")
455 error("class must be a descendant of AbstractValue")
459 -- Parse optional options
460 function AbstractSection.parse_optionals(self, section)
461 if not self.optional then
465 self.optionals[section] = {}
467 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
468 for k,v in ipairs(self.children) do
469 if v.optional and not v:cfgvalue(section) then
470 if field == v.option then
473 table.insert(self.optionals[section], v)
478 if field and #field > 0 and self.dynamic then
479 self:add_dynamic(field)
483 -- Add a dynamic option
484 function AbstractSection.add_dynamic(self, field, optional)
485 local o = self:option(Value, field, field)
486 o.optional = optional
489 -- Parse all dynamic options
490 function AbstractSection.parse_dynamic(self, section)
491 if not self.dynamic then
495 local arr = luci.util.clone(self:cfgvalue(section))
496 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
497 for k, v in pairs(form) do
501 for key,val in pairs(arr) do
504 for i,c in ipairs(self.children) do
505 if c.option == key then
510 if create and key:sub(1, 1) ~= "." then
511 self:add_dynamic(key, true)
516 -- Returns the section's UCI table
517 function AbstractSection.cfgvalue(self, section)
518 return self.map:get(section)
521 -- Removes the section
522 function AbstractSection.remove(self, section)
523 return self.map:del(section)
526 -- Creates the section
527 function AbstractSection.create(self, section)
531 stat = self.map:set(section, nil, self.sectiontype)
533 section = self.map:add(self.sectiontype)
538 for k,v in pairs(self.children) do
540 self.map:set(section, v.option, v.default)
544 for k,v in pairs(self.defaults) do
545 self.map:set(section, k, v)
553 SimpleSection = class(AbstractSection)
555 function SimpleSection.__init__(self, form, ...)
556 AbstractSection.__init__(self, form, nil, ...)
557 self.template = "cbi/nullsection"
561 Table = class(AbstractSection)
563 function Table.__init__(self, form, data, ...)
564 local datasource = {}
565 datasource.config = "table"
568 function datasource.get(self, section, option)
569 return data[section] and data[section][option]
572 function datasource.del(...)
576 function datasource.get_scheme()
580 AbstractSection.__init__(self, datasource, "table", ...)
581 self.template = "cbi/tblsection"
582 self.rowcolors = true
583 self.anonymous = true
586 function Table.parse(self)
587 for i, k in ipairs(self:cfgsections()) do
588 if luci.http.formvalue("cbi.submit") then
594 function Table.cfgsections(self)
597 for i, v in luci.util.kspairs(self.data) do
598 table.insert(sections, i)
607 NamedSection - A fixed configuration section defined by its name
609 NamedSection = class(AbstractSection)
611 function NamedSection.__init__(self, map, section, stype, ...)
612 AbstractSection.__init__(self, map, stype, ...)
613 Node._i18n(self, map.config, section, nil, ...)
616 self.addremove = false
618 -- Use defaults from UVL
619 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
620 local vs = self.map:get_scheme(self.sectiontype)
621 self.addremove = not vs.unique and not vs.required
622 self.dynamic = vs.dynamic
623 self.title = self.title or vs.title
624 self.description = self.description or vs.descr
627 self.template = "cbi/nsection"
628 self.section = section
631 function NamedSection.parse(self)
632 local s = self.section
633 local active = self:cfgvalue(s)
635 if self.addremove then
636 local path = self.config.."."..s
637 if active then -- Remove the section
638 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
641 else -- Create and apply default values
642 if luci.http.formvalue("cbi.cns."..path) then
650 AbstractSection.parse_dynamic(self, s)
651 if luci.http.formvalue("cbi.submit") then
654 if not self.override_scheme and self.map.scheme then
655 _uvl_validate_section(self, s)
658 AbstractSection.parse_optionals(self, s)
664 TypedSection - A (set of) configuration section(s) defined by the type
665 addremove: Defines whether the user can add/remove sections of this type
666 anonymous: Allow creating anonymous sections
667 validate: a validation function returning nil if the section is invalid
669 TypedSection = class(AbstractSection)
671 function TypedSection.__init__(self, map, type, ...)
672 AbstractSection.__init__(self, map, type, ...)
673 Node._i18n(self, map.config, type, nil, ...)
675 self.template = "cbi/tsection"
677 self.anonymous = false
679 -- Use defaults from UVL
680 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
681 local vs = self.map:get_scheme(self.sectiontype)
682 self.addremove = not vs.unique and not vs.required
683 self.dynamic = vs.dynamic
684 self.anonymous = not vs.named
685 self.title = self.title or vs.title
686 self.description = self.description or vs.descr
690 -- Return all matching UCI sections for this TypedSection
691 function TypedSection.cfgsections(self)
693 self.map.uci:foreach(self.map.config, self.sectiontype,
695 if self:checkscope(section[".name"]) then
696 table.insert(sections, section[".name"])
703 -- Limits scope to sections that have certain option => value pairs
704 function TypedSection.depends(self, option, value)
705 table.insert(self.deps, {option=option, value=value})
708 function TypedSection.parse(self)
709 if self.addremove then
711 local crval = REMOVE_PREFIX .. self.config
712 local name = luci.http.formvaluetable(crval)
713 for k,v in pairs(name) do
714 if k:sub(-2) == ".x" then
717 if self:cfgvalue(k) and self:checkscope(k) then
724 for i, k in ipairs(self:cfgsections()) do
725 AbstractSection.parse_dynamic(self, k)
726 if luci.http.formvalue("cbi.submit") then
729 if not self.override_scheme and self.map.scheme then
730 _uvl_validate_section(self, k)
733 AbstractSection.parse_optionals(self, k)
736 if self.addremove then
739 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
740 local name = luci.http.formvalue(crval)
741 if self.anonymous then
743 created = self:create()
747 -- Ignore if it already exists
748 if self:cfgvalue(name) then
752 name = self:checkscope(name)
755 self.err_invalid = true
758 if name and #name > 0 then
759 created = self:create(name) and name
765 AbstractSection.parse_optionals(self, created)
770 -- Verifies scope of sections
771 function TypedSection.checkscope(self, section)
772 -- Check if we are not excluded
773 if self.filter and not self:filter(section) then
777 -- Check if at least one dependency is met
778 if #self.deps > 0 and self:cfgvalue(section) then
781 for k, v in ipairs(self.deps) do
782 if self:cfgvalue(section)[v.option] == v.value then
792 return self:validate(section)
796 -- Dummy validate function
797 function TypedSection.validate(self, section)
803 AbstractValue - An abstract Value Type
804 null: Value can be empty
805 valid: A function returning the value if it is valid otherwise nil
806 depends: A table of option => value pairs of which one must be true
807 default: The default value
808 size: The size of the input fields
809 rmempty: Unset value if empty
810 optional: This value is optional (see AbstractSection.optionals)
812 AbstractValue = class(Node)
814 function AbstractValue.__init__(self, map, section, option, ...)
815 Node.__init__(self, ...)
816 self.section = section
819 self.config = map.config
820 self.tag_invalid = {}
821 self.tag_missing = {}
822 self.tag_reqerror = {}
827 self.track_missing = false
831 self.optional = false
833 -- Use defaults from UVL
834 if not self.override_scheme
835 and self.map:get_scheme(self.section.sectiontype, self.option) then
836 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
837 self.rmempty = not vs.required
838 self.cast = (vs.type == "list") and "list" or "string"
839 self.title = self.title or vs.title
840 self.description = self.description or vs.descr
841 self.default = vs.default
843 if vs.depends and not self.override_dependencies then
844 for i, deps in ipairs(vs.depends) do
845 deps = _uvl_strip_remote_dependencies(deps)
854 -- Add a dependencie to another section field
855 function AbstractValue.depends(self, field, value)
857 if type(field) == "string" then
864 table.insert(self.deps, {deps=deps, add=""})
867 -- Generates the unique CBID
868 function AbstractValue.cbid(self, section)
869 return "cbid."..self.map.config.."."..section.."."..self.option
872 -- Return whether this object should be created
873 function AbstractValue.formcreated(self, section)
874 local key = "cbi.opt."..self.config.."."..section
875 return (luci.http.formvalue(key) == self.option)
878 -- Returns the formvalue for this object
879 function AbstractValue.formvalue(self, section)
880 return luci.http.formvalue(self:cbid(section))
883 function AbstractValue.additional(self, value)
884 self.optional = value
887 function AbstractValue.mandatory(self, value)
888 self.rmempty = not value
891 function AbstractValue.parse(self, section)
892 local fvalue = self:formvalue(section)
893 local cvalue = self:cfgvalue(section)
895 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
896 fvalue = self:transform(self:validate(fvalue, section))
898 self.tag_invalid[section] = true
900 if fvalue and not (fvalue == cvalue) then
901 self:write(section, fvalue)
903 else -- Unset the UCI or error
904 if self.rmempty or self.optional then
906 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
907 self.tag_missing[section] = true
912 -- Render if this value exists or if it is mandatory
913 function AbstractValue.render(self, s, scope)
914 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
917 scope.cbid = self:cbid(s)
918 scope.striptags = luci.util.striptags
920 scope.ifattr = function(cond,key,val)
922 return string.format(
923 ' %s="%s"', tostring(key),
924 luci.util.pcdata(tostring( val
926 or (type(self[key]) ~= "function" and self[key])
934 scope.attr = function(...)
935 return scope.ifattr( true, ... )
938 Node.render(self, scope)
942 -- Return the UCI value of this object
943 function AbstractValue.cfgvalue(self, section)
944 local value = self.map:get(section, self.option)
945 if not self.cast or self.cast == type(value) then
947 elseif self.cast == "string" then
948 if type(value) == "table" then
951 elseif self.cast == "table" then
956 -- Validate the form value
957 function AbstractValue.validate(self, value)
961 AbstractValue.transform = AbstractValue.validate
965 function AbstractValue.write(self, section, value)
966 return self.map:set(section, self.option, value)
970 function AbstractValue.remove(self, section)
971 return self.map:del(section, self.option)
978 Value - A one-line value
979 maxlength: The maximum length
981 Value = class(AbstractValue)
983 function Value.__init__(self, ...)
984 AbstractValue.__init__(self, ...)
985 self.template = "cbi/value"
990 function Value.value(self, key, val)
992 table.insert(self.keylist, tostring(key))
993 table.insert(self.vallist, tostring(val))
997 -- DummyValue - This does nothing except being there
998 DummyValue = class(AbstractValue)
1000 function DummyValue.__init__(self, ...)
1001 AbstractValue.__init__(self, ...)
1002 self.template = "cbi/dvalue"
1006 function DummyValue.parse(self)
1012 Flag - A flag being enabled or disabled
1014 Flag = class(AbstractValue)
1016 function Flag.__init__(self, ...)
1017 AbstractValue.__init__(self, ...)
1018 self.template = "cbi/fvalue"
1024 -- A flag can only have two states: set or unset
1025 function Flag.parse(self, section)
1026 local fvalue = self:formvalue(section)
1029 fvalue = self.enabled
1031 fvalue = self.disabled
1034 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1035 if not(fvalue == self:cfgvalue(section)) then
1036 self:write(section, fvalue)
1039 self:remove(section)
1046 ListValue - A one-line value predefined in a list
1047 widget: The widget that will be used (select, radio)
1049 ListValue = class(AbstractValue)
1051 function ListValue.__init__(self, ...)
1052 AbstractValue.__init__(self, ...)
1053 self.template = "cbi/lvalue"
1058 self.widget = "select"
1060 if not self.override_scheme
1061 and self.map:get_scheme(self.section.sectiontype, self.option) then
1062 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1063 if self.value and vs.values and not self.override_values then
1064 if self.rmempty or self.optional then
1067 for k, v in pairs(vs.values) do
1069 if not self.override_dependencies
1070 and vs.enum_depends and vs.enum_depends[k] then
1071 for i, dep in ipairs(vs.enum_depends[k]) do
1072 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1075 self:value(k, v, unpack(deps))
1081 function ListValue.value(self, key, val, ...)
1082 if luci.util.contains(self.keylist, key) then
1087 table.insert(self.keylist, tostring(key))
1088 table.insert(self.vallist, tostring(val))
1090 for i, deps in ipairs({...}) do
1091 table.insert(self.deps, {add = "-"..key, deps=deps})
1095 function ListValue.validate(self, val)
1096 if luci.util.contains(self.keylist, val) then
1106 MultiValue - Multiple delimited values
1107 widget: The widget that will be used (select, checkbox)
1108 delimiter: The delimiter that will separate the values (default: " ")
1110 MultiValue = class(AbstractValue)
1112 function MultiValue.__init__(self, ...)
1113 AbstractValue.__init__(self, ...)
1114 self.template = "cbi/mvalue"
1119 self.widget = "checkbox"
1120 self.delimiter = " "
1123 function MultiValue.render(self, ...)
1124 if self.widget == "select" and not self.size then
1125 self.size = #self.vallist
1128 AbstractValue.render(self, ...)
1131 function MultiValue.value(self, key, val)
1132 if luci.util.contains(self.keylist, key) then
1137 table.insert(self.keylist, tostring(key))
1138 table.insert(self.vallist, tostring(val))
1141 function MultiValue.valuelist(self, section)
1142 local val = self:cfgvalue(section)
1144 if not(type(val) == "string") then
1148 return luci.util.split(val, self.delimiter)
1151 function MultiValue.validate(self, val)
1152 val = (type(val) == "table") and val or {val}
1156 for i, value in ipairs(val) do
1157 if luci.util.contains(self.keylist, value) then
1158 result = result and (result .. self.delimiter .. value) or value
1166 StaticList = class(MultiValue)
1168 function StaticList.__init__(self, ...)
1169 MultiValue.__init__(self, ...)
1171 self.valuelist = self.cfgvalue
1173 if not self.override_scheme
1174 and self.map:get_scheme(self.section.sectiontype, self.option) then
1175 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1176 if self.value and vs.values and not self.override_values then
1177 for k, v in pairs(vs.values) do
1184 function StaticList.validate(self, value)
1185 value = (type(value) == "table") and value or {value}
1188 for i, v in ipairs(value) do
1189 if luci.util.contains(self.valuelist, v) then
1190 table.insert(valid, v)
1197 DynamicList = class(AbstractValue)
1199 function DynamicList.__init__(self, ...)
1200 AbstractValue.__init__(self, ...)
1201 self.template = "cbi/dynlist"
1207 function DynamicList.value(self, key, val)
1209 table.insert(self.keylist, tostring(key))
1210 table.insert(self.vallist, tostring(val))
1213 function DynamicList.validate(self, value, section)
1214 value = (type(value) == "table") and value or {value}
1217 for i, v in ipairs(value) do
1219 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1220 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1221 table.insert(valid, v)
1230 TextValue - A multi-line value
1233 TextValue = class(AbstractValue)
1235 function TextValue.__init__(self, ...)
1236 AbstractValue.__init__(self, ...)
1237 self.template = "cbi/tvalue"
1243 Button = class(AbstractValue)
1245 function Button.__init__(self, ...)
1246 AbstractValue.__init__(self, ...)
1247 self.template = "cbi/button"
1248 self.inputstyle = nil