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")
61 translate=i18n.translate,
62 translatef=i18n.translatef,
66 setfenv(func, setmetatable(env, {__index =
68 return rawget(tbl, key) or _M[key] or _G[key]
73 for i, map in ipairs(maps) do
74 if not instanceof(map, Node) then
75 error("CBI map returns no valid map object!")
83 local function _uvl_validate_section(node, name)
84 local co = node.map:get()
86 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
87 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
89 local stat, err = node.map.validator:validate_section(node.config, name, co)
92 if err:is(luci.uvl.errors.ERR_DEPENDENCY) then
93 node.tag_deperror[name] = true
95 node.tag_invalid[name] = true
97 for i, v in ipairs(err.childs) do
98 if v.option and node.fields[v.option] then
99 if v:is(luci.uvl.errors.ERR_DEPENDENCY) then
100 node.fields[v.option].tag_reqerror[name] = true
101 elseif v:is(luci.uvl.errors.ERR_OPT_REQUIRED) then
102 node.fields[v.option].tag_missing[name] = true
103 node.tag_deperror[name] = true
104 elseif v:is(luci.uvl.errors.ERR_OPTION) then
105 node.fields[v.option].tag_invalid[name] = true
113 local function _uvl_strip_remote_dependencies(deps)
116 for k, v in pairs(deps) do
117 k = k:gsub("%$config%.%$section%.", "")
118 if k:match("^[%w_]+$") and type(v) == "string" then
127 -- Node pseudo abstract class
130 function Node.__init__(self, title, description)
132 self.title = title or ""
133 self.description = description or ""
134 self.template = "cbi/node"
138 function Node._i18n(self, config, section, option, title, description)
141 if type(luci.i18n) == "table" then
143 local key = config and config:gsub("[^%w]+", "") or ""
145 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
146 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
148 self.title = title or luci.i18n.translate( key, option or section or config )
149 self.description = description or luci.i18n.translate( key .. "_desc", "" )
153 -- Append child nodes
154 function Node.append(self, obj)
155 table.insert(self.children, obj)
158 -- Parse this node and its children
159 function Node.parse(self, ...)
160 for k, child in ipairs(self.children) do
166 function Node.render(self, scope)
170 luci.template.render(self.template, scope)
173 -- Render the children
174 function Node.render_children(self, ...)
175 for k, node in ipairs(self.children) do
182 A simple template element
184 Template = class(Node)
186 function Template.__init__(self, template)
188 self.template = template
191 function Template.render(self)
192 luci.template.render(self.template, {self=self})
197 Map - A map describing a configuration file
201 function Map.__init__(self, config, ...)
202 Node.__init__(self, ...)
203 Node._i18n(self, config, nil, nil, ...)
206 self.parsechain = {self.config}
207 self.template = "cbi/map"
208 self.uci = uci.cursor()
210 if not self.uci:load(self.config) then
211 error("Unable to read UCI data: " .. self.config)
214 self.validator = luci.uvl.UVL()
215 self.scheme = self.validator:get_scheme(self.config)
219 function Map.get_scheme(self, sectiontype, option)
221 return self.scheme and self.scheme.sections[sectiontype]
223 return self.scheme and self.scheme.variables[sectiontype]
224 and self.scheme.variables[sectiontype][option]
229 -- Chain foreign config
230 function Map.chain(self, config)
231 table.insert(self.parsechain, config)
234 -- Use optimized UCI writing
235 function Map.parse(self, ...)
236 Node.parse(self, ...)
239 for i, config in ipairs(self.parsechain) do
240 self.uci:save(config)
242 if luci.http.formvalue("cbi.apply") then
243 for i, config in ipairs(self.parsechain) do
244 self.uci:commit(config)
245 self.uci:apply(config)
247 -- Refresh data because commit changes section names
248 self.uci:load(config)
252 Node.parse(self, ...)
255 for i, config in ipairs(self.parsechain) do
256 self.uci:unload(config)
261 -- Creates a child section
262 function Map.section(self, class, ...)
263 if instanceof(class, AbstractSection) then
264 local obj = class(self, ...)
268 error("class must be a descendent of AbstractSection")
273 function Map.add(self, sectiontype)
274 return self.uci:add(self.config, sectiontype)
278 function Map.set(self, section, option, value)
280 return self.uci:set(self.config, section, option, value)
282 return self.uci:set(self.config, section, value)
287 function Map.del(self, section, option)
289 return self.uci:delete(self.config, section, option)
291 return self.uci:delete(self.config, section)
296 function Map.get(self, section, option)
298 return self.uci:get_all(self.config)
300 return self.uci:get(self.config, section, option)
302 return self.uci:get_all(self.config, section)
312 Page.__init__ = Node.__init__
313 Page.parse = function() end
317 SimpleForm - A Simple non-UCI form
319 SimpleForm = class(Node)
321 function SimpleForm.__init__(self, config, title, description, data)
322 Node.__init__(self, title, description)
324 self.data = data or {}
325 self.template = "cbi/simpleform"
329 function SimpleForm.parse(self, ...)
330 if luci.http.formvalue("cbi.submit") then
331 Node.parse(self, 1, ...)
335 for k, j in ipairs(self.children) do
336 for i, v in ipairs(j.children) do
338 and (not v.tag_missing or not v.tag_missing[1])
339 and (not v.tag_invalid or not v.tag_invalid[1])
344 not luci.http.formvalue("cbi.submit") and 0
348 self.dorender = not self.handle or self:handle(state, self.data) ~= false
351 function SimpleForm.render(self, ...)
352 if self.dorender then
353 Node.render(self, ...)
357 function SimpleForm.section(self, class, ...)
358 if instanceof(class, AbstractSection) then
359 local obj = class(self, ...)
363 error("class must be a descendent of AbstractSection")
367 -- Creates a child field
368 function SimpleForm.field(self, class, ...)
370 for k, v in ipairs(self.children) do
371 if instanceof(v, SimpleSection) then
377 section = self:section(SimpleSection)
380 if instanceof(class, AbstractValue) then
381 local obj = class(self, section, ...)
382 obj.track_missing = true
386 error("class must be a descendent of AbstractValue")
390 function SimpleForm.set(self, section, option, value)
391 self.data[option] = value
395 function SimpleForm.del(self, section, option)
396 self.data[option] = nil
400 function SimpleForm.get(self, section, option)
401 return self.data[option]
405 function SimpleForm.get_scheme()
414 AbstractSection = class(Node)
416 function AbstractSection.__init__(self, map, sectiontype, ...)
417 Node.__init__(self, ...)
418 self.sectiontype = sectiontype
420 self.config = map.config
425 self.tag_invalid = {}
426 self.tag_deperror = {}
429 self.addremove = false
433 -- Appends a new option
434 function AbstractSection.option(self, class, option, ...)
435 -- Autodetect from UVL
436 if class == true and self.map:get_scheme(self.sectiontype, option) then
437 local vs = self.map:get_scheme(self.sectiontype, option)
438 if vs.type == "boolean" then
440 elseif vs.type == "list" then
442 elseif vs.type == "enum" or vs.type == "reference" then
449 if instanceof(class, AbstractValue) then
450 local obj = class(self.map, self, option, ...)
452 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
455 self.fields[option] = obj
457 elseif class == true then
458 error("No valid class was given and autodetection failed.")
460 error("class must be a descendant of AbstractValue")
464 -- Parse optional options
465 function AbstractSection.parse_optionals(self, section)
466 if not self.optional then
470 self.optionals[section] = {}
472 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
473 for k,v in ipairs(self.children) do
474 if v.optional and not v:cfgvalue(section) then
475 if field == v.option then
478 table.insert(self.optionals[section], v)
483 if field and #field > 0 and self.dynamic then
484 self:add_dynamic(field)
488 -- Add a dynamic option
489 function AbstractSection.add_dynamic(self, field, optional)
490 local o = self:option(Value, field, field)
491 o.optional = optional
494 -- Parse all dynamic options
495 function AbstractSection.parse_dynamic(self, section)
496 if not self.dynamic then
500 local arr = luci.util.clone(self:cfgvalue(section))
501 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
502 for k, v in pairs(form) do
506 for key,val in pairs(arr) do
509 for i,c in ipairs(self.children) do
510 if c.option == key then
515 if create and key:sub(1, 1) ~= "." then
516 self:add_dynamic(key, true)
521 -- Returns the section's UCI table
522 function AbstractSection.cfgvalue(self, section)
523 return self.map:get(section)
526 -- Removes the section
527 function AbstractSection.remove(self, section)
528 return self.map:del(section)
531 -- Creates the section
532 function AbstractSection.create(self, section)
536 stat = self.map:set(section, nil, self.sectiontype)
538 section = self.map:add(self.sectiontype)
543 for k,v in pairs(self.children) do
545 self.map:set(section, v.option, v.default)
549 for k,v in pairs(self.defaults) do
550 self.map:set(section, k, v)
558 SimpleSection = class(AbstractSection)
560 function SimpleSection.__init__(self, form, ...)
561 AbstractSection.__init__(self, form, nil, ...)
562 self.template = "cbi/nullsection"
566 Table = class(AbstractSection)
568 function Table.__init__(self, form, data, ...)
569 local datasource = {}
570 datasource.config = "table"
573 function datasource.get(self, section, option)
574 return data[section] and data[section][option]
577 function datasource.del(...)
581 function datasource.get_scheme()
585 AbstractSection.__init__(self, datasource, "table", ...)
586 self.template = "cbi/tblsection"
587 self.rowcolors = true
588 self.anonymous = true
591 function Table.parse(self)
592 for i, k in ipairs(self:cfgsections()) do
593 if luci.http.formvalue("cbi.submit") then
599 function Table.cfgsections(self)
602 for i, v in luci.util.kspairs(self.data) do
603 table.insert(sections, i)
612 NamedSection - A fixed configuration section defined by its name
614 NamedSection = class(AbstractSection)
616 function NamedSection.__init__(self, map, section, stype, ...)
617 AbstractSection.__init__(self, map, stype, ...)
618 Node._i18n(self, map.config, section, nil, ...)
621 self.addremove = false
623 -- Use defaults from UVL
624 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
625 local vs = self.map:get_scheme(self.sectiontype)
626 self.addremove = not vs.unique and not vs.required
627 self.dynamic = vs.dynamic
628 self.title = self.title or vs.title
629 self.description = self.description or vs.descr
632 self.template = "cbi/nsection"
633 self.section = section
636 function NamedSection.parse(self)
637 local s = self.section
638 local active = self:cfgvalue(s)
640 if self.addremove then
641 local path = self.config.."."..s
642 if active then -- Remove the section
643 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
646 else -- Create and apply default values
647 if luci.http.formvalue("cbi.cns."..path) then
655 AbstractSection.parse_dynamic(self, s)
656 if luci.http.formvalue("cbi.submit") then
659 if not self.override_scheme and self.map.scheme then
660 _uvl_validate_section(self, s)
663 AbstractSection.parse_optionals(self, s)
669 TypedSection - A (set of) configuration section(s) defined by the type
670 addremove: Defines whether the user can add/remove sections of this type
671 anonymous: Allow creating anonymous sections
672 validate: a validation function returning nil if the section is invalid
674 TypedSection = class(AbstractSection)
676 function TypedSection.__init__(self, map, type, ...)
677 AbstractSection.__init__(self, map, type, ...)
678 Node._i18n(self, map.config, type, nil, ...)
680 self.template = "cbi/tsection"
682 self.anonymous = false
684 -- Use defaults from UVL
685 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
686 local vs = self.map:get_scheme(self.sectiontype)
687 self.addremove = not vs.unique and not vs.required
688 self.dynamic = vs.dynamic
689 self.anonymous = not vs.named
690 self.title = self.title or vs.title
691 self.description = self.description or vs.descr
695 -- Return all matching UCI sections for this TypedSection
696 function TypedSection.cfgsections(self)
698 self.map.uci:foreach(self.map.config, self.sectiontype,
700 if self:checkscope(section[".name"]) then
701 table.insert(sections, section[".name"])
708 -- Limits scope to sections that have certain option => value pairs
709 function TypedSection.depends(self, option, value)
710 table.insert(self.deps, {option=option, value=value})
713 function TypedSection.parse(self)
714 if self.addremove then
716 local crval = REMOVE_PREFIX .. self.config
717 local name = luci.http.formvaluetable(crval)
718 for k,v in pairs(name) do
719 if k:sub(-2) == ".x" then
722 if self:cfgvalue(k) and self:checkscope(k) then
729 for i, k in ipairs(self:cfgsections()) do
730 AbstractSection.parse_dynamic(self, k)
731 if luci.http.formvalue("cbi.submit") then
734 if not self.override_scheme and self.map.scheme then
735 _uvl_validate_section(self, k)
738 AbstractSection.parse_optionals(self, k)
741 if self.addremove then
744 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
745 local name = luci.http.formvalue(crval)
746 if self.anonymous then
748 created = self:create()
752 -- Ignore if it already exists
753 if self:cfgvalue(name) then
757 name = self:checkscope(name)
760 self.err_invalid = true
763 if name and #name > 0 then
764 created = self:create(name) and name
770 AbstractSection.parse_optionals(self, created)
775 -- Verifies scope of sections
776 function TypedSection.checkscope(self, section)
777 -- Check if we are not excluded
778 if self.filter and not self:filter(section) then
782 -- Check if at least one dependency is met
783 if #self.deps > 0 and self:cfgvalue(section) then
786 for k, v in ipairs(self.deps) do
787 if self:cfgvalue(section)[v.option] == v.value then
797 return self:validate(section)
801 -- Dummy validate function
802 function TypedSection.validate(self, section)
808 AbstractValue - An abstract Value Type
809 null: Value can be empty
810 valid: A function returning the value if it is valid otherwise nil
811 depends: A table of option => value pairs of which one must be true
812 default: The default value
813 size: The size of the input fields
814 rmempty: Unset value if empty
815 optional: This value is optional (see AbstractSection.optionals)
817 AbstractValue = class(Node)
819 function AbstractValue.__init__(self, map, section, option, ...)
820 Node.__init__(self, ...)
821 self.section = section
824 self.config = map.config
825 self.tag_invalid = {}
826 self.tag_missing = {}
827 self.tag_reqerror = {}
832 self.track_missing = false
836 self.optional = false
838 -- Use defaults from UVL
839 if not self.override_scheme
840 and self.map:get_scheme(self.section.sectiontype, self.option) then
841 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
842 self.rmempty = not vs.required
843 self.cast = (vs.type == "list") and "list" or "string"
844 self.title = self.title or vs.title
845 self.description = self.description or vs.descr
846 self.default = vs.default
848 if vs.depends and not self.override_dependencies then
849 for i, deps in ipairs(vs.depends) do
850 deps = _uvl_strip_remote_dependencies(deps)
859 -- Add a dependencie to another section field
860 function AbstractValue.depends(self, field, value)
862 if type(field) == "string" then
869 table.insert(self.deps, {deps=deps, add=""})
872 -- Generates the unique CBID
873 function AbstractValue.cbid(self, section)
874 return "cbid."..self.map.config.."."..section.."."..self.option
877 -- Return whether this object should be created
878 function AbstractValue.formcreated(self, section)
879 local key = "cbi.opt."..self.config.."."..section
880 return (luci.http.formvalue(key) == self.option)
883 -- Returns the formvalue for this object
884 function AbstractValue.formvalue(self, section)
885 return luci.http.formvalue(self:cbid(section))
888 function AbstractValue.additional(self, value)
889 self.optional = value
892 function AbstractValue.mandatory(self, value)
893 self.rmempty = not value
896 function AbstractValue.parse(self, section)
897 local fvalue = self:formvalue(section)
898 local cvalue = self:cfgvalue(section)
900 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
901 fvalue = self:transform(self:validate(fvalue, section))
903 self.tag_invalid[section] = true
905 if fvalue and not (fvalue == cvalue) then
906 self:write(section, fvalue)
908 else -- Unset the UCI or error
909 if self.rmempty or self.optional then
911 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
912 self.tag_missing[section] = true
917 -- Render if this value exists or if it is mandatory
918 function AbstractValue.render(self, s, scope)
919 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
922 scope.cbid = self:cbid(s)
923 scope.striptags = luci.util.striptags
925 scope.ifattr = function(cond,key,val)
927 return string.format(
928 ' %s="%s"', tostring(key),
929 luci.util.pcdata(tostring( val
931 or (type(self[key]) ~= "function" and self[key])
939 scope.attr = function(...)
940 return scope.ifattr( true, ... )
943 Node.render(self, scope)
947 -- Return the UCI value of this object
948 function AbstractValue.cfgvalue(self, section)
949 local value = self.map:get(section, self.option)
950 if not self.cast or self.cast == type(value) then
952 elseif self.cast == "string" then
953 if type(value) == "table" then
956 elseif self.cast == "table" then
961 -- Validate the form value
962 function AbstractValue.validate(self, value)
966 AbstractValue.transform = AbstractValue.validate
970 function AbstractValue.write(self, section, value)
971 return self.map:set(section, self.option, value)
975 function AbstractValue.remove(self, section)
976 return self.map:del(section, self.option)
983 Value - A one-line value
984 maxlength: The maximum length
986 Value = class(AbstractValue)
988 function Value.__init__(self, ...)
989 AbstractValue.__init__(self, ...)
990 self.template = "cbi/value"
995 function Value.value(self, key, val)
997 table.insert(self.keylist, tostring(key))
998 table.insert(self.vallist, tostring(val))
1002 -- DummyValue - This does nothing except being there
1003 DummyValue = class(AbstractValue)
1005 function DummyValue.__init__(self, ...)
1006 AbstractValue.__init__(self, ...)
1007 self.template = "cbi/dvalue"
1011 function DummyValue.parse(self)
1017 Flag - A flag being enabled or disabled
1019 Flag = class(AbstractValue)
1021 function Flag.__init__(self, ...)
1022 AbstractValue.__init__(self, ...)
1023 self.template = "cbi/fvalue"
1029 -- A flag can only have two states: set or unset
1030 function Flag.parse(self, section)
1031 local fvalue = self:formvalue(section)
1034 fvalue = self.enabled
1036 fvalue = self.disabled
1039 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1040 if not(fvalue == self:cfgvalue(section)) then
1041 self:write(section, fvalue)
1044 self:remove(section)
1051 ListValue - A one-line value predefined in a list
1052 widget: The widget that will be used (select, radio)
1054 ListValue = class(AbstractValue)
1056 function ListValue.__init__(self, ...)
1057 AbstractValue.__init__(self, ...)
1058 self.template = "cbi/lvalue"
1063 self.widget = "select"
1065 if not self.override_scheme
1066 and self.map:get_scheme(self.section.sectiontype, self.option) then
1067 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1068 if self.value and vs.values and not self.override_values then
1069 if self.rmempty or self.optional then
1072 for k, v in pairs(vs.values) do
1074 if not self.override_dependencies
1075 and vs.enum_depends and vs.enum_depends[k] then
1076 for i, dep in ipairs(vs.enum_depends[k]) do
1077 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1080 self:value(k, v, unpack(deps))
1086 function ListValue.value(self, key, val, ...)
1087 if luci.util.contains(self.keylist, key) then
1092 table.insert(self.keylist, tostring(key))
1093 table.insert(self.vallist, tostring(val))
1095 for i, deps in ipairs({...}) do
1096 table.insert(self.deps, {add = "-"..key, deps=deps})
1100 function ListValue.validate(self, val)
1101 if luci.util.contains(self.keylist, val) then
1111 MultiValue - Multiple delimited values
1112 widget: The widget that will be used (select, checkbox)
1113 delimiter: The delimiter that will separate the values (default: " ")
1115 MultiValue = class(AbstractValue)
1117 function MultiValue.__init__(self, ...)
1118 AbstractValue.__init__(self, ...)
1119 self.template = "cbi/mvalue"
1124 self.widget = "checkbox"
1125 self.delimiter = " "
1128 function MultiValue.render(self, ...)
1129 if self.widget == "select" and not self.size then
1130 self.size = #self.vallist
1133 AbstractValue.render(self, ...)
1136 function MultiValue.value(self, key, val)
1137 if luci.util.contains(self.keylist, key) then
1142 table.insert(self.keylist, tostring(key))
1143 table.insert(self.vallist, tostring(val))
1146 function MultiValue.valuelist(self, section)
1147 local val = self:cfgvalue(section)
1149 if not(type(val) == "string") then
1153 return luci.util.split(val, self.delimiter)
1156 function MultiValue.validate(self, val)
1157 val = (type(val) == "table") and val or {val}
1161 for i, value in ipairs(val) do
1162 if luci.util.contains(self.keylist, value) then
1163 result = result and (result .. self.delimiter .. value) or value
1171 StaticList = class(MultiValue)
1173 function StaticList.__init__(self, ...)
1174 MultiValue.__init__(self, ...)
1176 self.valuelist = self.cfgvalue
1178 if not self.override_scheme
1179 and self.map:get_scheme(self.section.sectiontype, self.option) then
1180 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1181 if self.value and vs.values and not self.override_values then
1182 for k, v in pairs(vs.values) do
1189 function StaticList.validate(self, value)
1190 value = (type(value) == "table") and value or {value}
1193 for i, v in ipairs(value) do
1194 if luci.util.contains(self.valuelist, v) then
1195 table.insert(valid, v)
1202 DynamicList = class(AbstractValue)
1204 function DynamicList.__init__(self, ...)
1205 AbstractValue.__init__(self, ...)
1206 self.template = "cbi/dynlist"
1212 function DynamicList.value(self, key, val)
1214 table.insert(self.keylist, tostring(key))
1215 table.insert(self.vallist, tostring(val))
1218 function DynamicList.validate(self, value, section)
1219 value = (type(value) == "table") and value or {value}
1222 for i, v in ipairs(value) do
1224 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1225 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1226 table.insert(valid, v)
1235 TextValue - A multi-line value
1238 TextValue = class(AbstractValue)
1240 function TextValue.__init__(self, ...)
1241 AbstractValue.__init__(self, ...)
1242 self.template = "cbi/tvalue"
1248 Button = class(AbstractValue)
1250 function Button.__init__(self, ...)
1251 AbstractValue.__init__(self, ...)
1252 self.template = "cbi/button"
1253 self.inputstyle = nil