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
842 if vs.depends and not self.override_dependencies then
843 for i, deps in ipairs(vs.depends) do
844 deps = _uvl_strip_remote_dependencies(deps)
853 -- Add a dependencie to another section field
854 function AbstractValue.depends(self, field, value)
856 if type(field) == "string" then
863 table.insert(self.deps, {deps=deps, add=""})
866 -- Generates the unique CBID
867 function AbstractValue.cbid(self, section)
868 return "cbid."..self.map.config.."."..section.."."..self.option
871 -- Return whether this object should be created
872 function AbstractValue.formcreated(self, section)
873 local key = "cbi.opt."..self.config.."."..section
874 return (luci.http.formvalue(key) == self.option)
877 -- Returns the formvalue for this object
878 function AbstractValue.formvalue(self, section)
879 return luci.http.formvalue(self:cbid(section))
882 function AbstractValue.additional(self, value)
883 self.optional = value
886 function AbstractValue.mandatory(self, value)
887 self.rmempty = not value
890 function AbstractValue.parse(self, section)
891 local fvalue = self:formvalue(section)
892 local cvalue = self:cfgvalue(section)
894 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
895 fvalue = self:transform(self:validate(fvalue, section))
897 self.tag_invalid[section] = true
899 if fvalue and not (fvalue == cvalue) then
900 self:write(section, fvalue)
902 else -- Unset the UCI or error
903 if self.rmempty or self.optional then
905 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
906 self.tag_missing[section] = true
911 -- Render if this value exists or if it is mandatory
912 function AbstractValue.render(self, s, scope)
913 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
916 scope.cbid = self:cbid(s)
917 scope.striptags = luci.util.striptags
919 scope.ifattr = function(cond,key,val)
921 return string.format(
922 ' %s="%s"', tostring(key),
923 luci.util.pcdata(tostring( val
925 or (type(self[key]) ~= "function" and self[key])
933 scope.attr = function(...)
934 return scope.ifattr( true, ... )
937 Node.render(self, scope)
941 -- Return the UCI value of this object
942 function AbstractValue.cfgvalue(self, section)
943 local value = self.map:get(section, self.option)
944 if not self.cast or self.cast == type(value) then
946 elseif self.cast == "string" then
947 if type(value) == "table" then
950 elseif self.cast == "table" then
955 -- Validate the form value
956 function AbstractValue.validate(self, value)
960 AbstractValue.transform = AbstractValue.validate
964 function AbstractValue.write(self, section, value)
965 return self.map:set(section, self.option, value)
969 function AbstractValue.remove(self, section)
970 return self.map:del(section, self.option)
977 Value - A one-line value
978 maxlength: The maximum length
980 Value = class(AbstractValue)
982 function Value.__init__(self, ...)
983 AbstractValue.__init__(self, ...)
984 self.template = "cbi/value"
989 function Value.value(self, key, val)
991 table.insert(self.keylist, tostring(key))
992 table.insert(self.vallist, tostring(val))
996 -- DummyValue - This does nothing except being there
997 DummyValue = class(AbstractValue)
999 function DummyValue.__init__(self, ...)
1000 AbstractValue.__init__(self, ...)
1001 self.template = "cbi/dvalue"
1005 function DummyValue.parse(self)
1011 Flag - A flag being enabled or disabled
1013 Flag = class(AbstractValue)
1015 function Flag.__init__(self, ...)
1016 AbstractValue.__init__(self, ...)
1017 self.template = "cbi/fvalue"
1023 -- A flag can only have two states: set or unset
1024 function Flag.parse(self, section)
1025 local fvalue = self:formvalue(section)
1028 fvalue = self.enabled
1030 fvalue = self.disabled
1033 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1034 if not(fvalue == self:cfgvalue(section)) then
1035 self:write(section, fvalue)
1038 self:remove(section)
1045 ListValue - A one-line value predefined in a list
1046 widget: The widget that will be used (select, radio)
1048 ListValue = class(AbstractValue)
1050 function ListValue.__init__(self, ...)
1051 AbstractValue.__init__(self, ...)
1052 self.template = "cbi/lvalue"
1057 self.widget = "select"
1059 if not self.override_scheme
1060 and self.map:get_scheme(self.section.sectiontype, self.option) then
1061 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1062 if self.value and vs.values and not self.override_values then
1063 if self.rmempty or self.optional then
1066 for k, v in pairs(vs.values) do
1068 if not self.override_dependencies
1069 and vs.enum_depends and vs.enum_depends[k] then
1070 for i, dep in ipairs(vs.enum_depends[k]) do
1071 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1074 self:value(k, v, unpack(deps))
1080 function ListValue.value(self, key, val, ...)
1081 if luci.util.contains(self.keylist, key) then
1086 table.insert(self.keylist, tostring(key))
1087 table.insert(self.vallist, tostring(val))
1089 for i, deps in ipairs({...}) do
1090 table.insert(self.deps, {add = "-"..key, deps=deps})
1094 function ListValue.validate(self, val)
1095 if luci.util.contains(self.keylist, val) then
1105 MultiValue - Multiple delimited values
1106 widget: The widget that will be used (select, checkbox)
1107 delimiter: The delimiter that will separate the values (default: " ")
1109 MultiValue = class(AbstractValue)
1111 function MultiValue.__init__(self, ...)
1112 AbstractValue.__init__(self, ...)
1113 self.template = "cbi/mvalue"
1118 self.widget = "checkbox"
1119 self.delimiter = " "
1122 function MultiValue.render(self, ...)
1123 if self.widget == "select" and not self.size then
1124 self.size = #self.vallist
1127 AbstractValue.render(self, ...)
1130 function MultiValue.value(self, key, val)
1131 if luci.util.contains(self.keylist, key) then
1136 table.insert(self.keylist, tostring(key))
1137 table.insert(self.vallist, tostring(val))
1140 function MultiValue.valuelist(self, section)
1141 local val = self:cfgvalue(section)
1143 if not(type(val) == "string") then
1147 return luci.util.split(val, self.delimiter)
1150 function MultiValue.validate(self, val)
1151 val = (type(val) == "table") and val or {val}
1155 for i, value in ipairs(val) do
1156 if luci.util.contains(self.keylist, value) then
1157 result = result and (result .. self.delimiter .. value) or value
1165 StaticList = class(MultiValue)
1167 function StaticList.__init__(self, ...)
1168 MultiValue.__init__(self, ...)
1170 self.valuelist = self.cfgvalue
1172 if not self.override_scheme
1173 and self.map:get_scheme(self.section.sectiontype, self.option) then
1174 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1175 if self.value and vs.values and not self.override_values then
1176 for k, v in pairs(vs.values) do
1183 function StaticList.validate(self, value)
1184 value = (type(value) == "table") and value or {value}
1187 for i, v in ipairs(value) do
1188 if luci.util.contains(self.valuelist, v) then
1189 table.insert(valid, v)
1196 DynamicList = class(AbstractValue)
1198 function DynamicList.__init__(self, ...)
1199 AbstractValue.__init__(self, ...)
1200 self.template = "cbi/dynlist"
1206 function DynamicList.value(self, key, val)
1208 table.insert(self.keylist, tostring(key))
1209 table.insert(self.vallist, tostring(val))
1212 function DynamicList.validate(self, value, section)
1213 value = (type(value) == "table") and value or {value}
1216 for i, v in ipairs(value) do
1218 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1219 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1220 table.insert(valid, v)
1229 TextValue - A multi-line value
1232 TextValue = class(AbstractValue)
1234 function TextValue.__init__(self, ...)
1235 AbstractValue.__init__(self, ...)
1236 self.template = "cbi/tvalue"
1242 Button = class(AbstractValue)
1244 function Button.__init__(self, ...)
1245 AbstractValue.__init__(self, ...)
1246 self.template = "cbi/button"
1247 self.inputstyle = nil