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(cbidir..cbimap..".lua")
61 luci.i18n.loadc("cbi")
63 luci.util.resfenv(func)
64 luci.util.updfenv(func, luci.cbi)
65 luci.util.extfenv(func, "translate", luci.i18n.translate)
66 luci.util.extfenv(func, "translatef", luci.i18n.translatef)
67 luci.util.extfenv(func, "arg", {...})
71 for i, map in ipairs(maps) do
72 if not instanceof(map, Node) then
73 error("CBI map returns no valid map object!")
81 local function _uvl_validate_section(node, name)
82 local co = node.map:get()
84 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
85 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
87 local stat, err = node.map.validator:validate_section(node.config, name, co)
90 if err:is(luci.uvl.errors.ERR_DEPENDENCY) then
91 node.tag_deperror[name] = true
93 node.tag_invalid[name] = true
95 for i, v in ipairs(err.childs) do
96 if v.option and node.fields[v.option] then
97 if v:is(luci.uvl.errors.ERR_DEPENDENCY) then
98 node.fields[v.option].tag_reqerror[name] = true
99 elseif v:is(luci.uvl.errors.ERR_OPT_REQUIRED) then
100 node.fields[v.option].tag_missing[name] = true
101 node.tag_deperror[name] = true
102 elseif v:is(luci.uvl.errors.ERR_OPTION) then
103 node.fields[v.option].tag_invalid[name] = true
111 local function _uvl_strip_remote_dependencies(deps)
114 for k, v in pairs(deps) do
115 k = k:gsub("%$config%.%$section%.", "")
116 if k:match("^[%w_]+$") and type(v) == "string" then
125 -- Node pseudo abstract class
128 function Node.__init__(self, title, description)
130 self.title = title or ""
131 self.description = description or ""
132 self.template = "cbi/node"
136 function Node._i18n(self, config, section, option, title, description)
139 if type(luci.i18n) == "table" then
141 local key = config and config:gsub("[^%w]+", "") or ""
143 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
144 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
146 self.title = title or luci.i18n.translate( key, option or section or config )
147 self.description = description or luci.i18n.translate( key .. "_desc", "" )
151 -- Append child nodes
152 function Node.append(self, obj)
153 table.insert(self.children, obj)
156 -- Parse this node and its children
157 function Node.parse(self, ...)
158 for k, child in ipairs(self.children) do
164 function Node.render(self, scope)
168 luci.template.render(self.template, scope)
171 -- Render the children
172 function Node.render_children(self, ...)
173 for k, node in ipairs(self.children) do
180 A simple template element
182 Template = class(Node)
184 function Template.__init__(self, template)
186 self.template = template
189 function Template.render(self)
190 luci.template.render(self.template, {self=self})
195 Map - A map describing a configuration file
199 function Map.__init__(self, config, ...)
200 Node.__init__(self, ...)
201 Node._i18n(self, config, nil, nil, ...)
204 self.parsechain = {self.config}
205 self.template = "cbi/map"
206 self.uci = uci.cursor()
208 if not self.uci:load(self.config) then
209 error("Unable to read UCI data: " .. self.config)
212 self.validator = luci.uvl.UVL()
213 self.scheme = self.validator:get_scheme(self.config)
217 function Map.get_scheme(self, sectiontype, option)
219 return self.scheme and self.scheme.sections[sectiontype]
221 return self.scheme and self.scheme.variables[sectiontype]
222 and self.scheme.variables[sectiontype][option]
227 -- Chain foreign config
228 function Map.chain(self, config)
229 table.insert(self.parsechain, config)
232 -- Use optimized UCI writing
233 function Map.parse(self, ...)
234 Node.parse(self, ...)
237 for i, config in ipairs(self.parsechain) do
238 self.uci:save(config)
240 if luci.http.formvalue("cbi.apply") then
241 for i, config in ipairs(self.parsechain) do
242 self.uci:commit(config)
243 self.uci:apply(config)
245 -- Refresh data because commit changes section names
246 self.uci:load(config)
250 Node.parse(self, ...)
253 for i, config in ipairs(self.parsechain) do
254 self.uci:unload(config)
259 -- Creates a child section
260 function Map.section(self, class, ...)
261 if instanceof(class, AbstractSection) then
262 local obj = class(self, ...)
266 error("class must be a descendent of AbstractSection")
271 function Map.add(self, sectiontype)
272 return self.uci:add(self.config, sectiontype)
276 function Map.set(self, section, option, value)
278 return self.uci:set(self.config, section, option, value)
280 return self.uci:set(self.config, section, value)
285 function Map.del(self, section, option)
287 return self.uci:delete(self.config, section, option)
289 return self.uci:delete(self.config, section)
294 function Map.get(self, section, option)
296 return self.uci:get_all(self.config)
298 return self.uci:get(self.config, section, option)
300 return self.uci:get_all(self.config, section)
310 Page.__init__ = Node.__init__
311 Page.parse = function() end
315 SimpleForm - A Simple non-UCI form
317 SimpleForm = class(Node)
319 function SimpleForm.__init__(self, config, title, description, data)
320 Node.__init__(self, title, description)
322 self.data = data or {}
323 self.template = "cbi/simpleform"
327 function SimpleForm.parse(self, ...)
328 if luci.http.formvalue("cbi.submit") then
329 Node.parse(self, 1, ...)
333 for k, j in ipairs(self.children) do
334 for i, v in ipairs(j.children) do
336 and (not v.tag_missing or not v.tag_missing[1])
337 and (not v.tag_invalid or not v.tag_invalid[1])
342 not luci.http.formvalue("cbi.submit") and 0
346 self.dorender = not self.handle or self:handle(state, self.data) ~= false
349 function SimpleForm.render(self, ...)
350 if self.dorender then
351 Node.render(self, ...)
355 function SimpleForm.section(self, class, ...)
356 if instanceof(class, AbstractSection) then
357 local obj = class(self, ...)
361 error("class must be a descendent of AbstractSection")
365 -- Creates a child field
366 function SimpleForm.field(self, class, ...)
368 for k, v in ipairs(self.children) do
369 if instanceof(v, SimpleSection) then
375 section = self:section(SimpleSection)
378 if instanceof(class, AbstractValue) then
379 local obj = class(self, section, ...)
380 obj.track_missing = true
384 error("class must be a descendent of AbstractValue")
388 function SimpleForm.set(self, section, option, value)
389 self.data[option] = value
393 function SimpleForm.del(self, section, option)
394 self.data[option] = nil
398 function SimpleForm.get(self, section, option)
399 return self.data[option]
403 function SimpleForm.get_scheme()
412 AbstractSection = class(Node)
414 function AbstractSection.__init__(self, map, sectiontype, ...)
415 Node.__init__(self, ...)
416 self.sectiontype = sectiontype
418 self.config = map.config
423 self.tag_invalid = {}
424 self.tag_deperror = {}
427 self.addremove = false
431 -- Appends a new option
432 function AbstractSection.option(self, class, option, ...)
433 -- Autodetect from UVL
434 if class == true and self.map:get_scheme(self.sectiontype, option) then
435 local vs = self.map:get_scheme(self.sectiontype, option)
436 if vs.type == "boolean" then
438 elseif vs.type == "list" then
440 elseif vs.type == "enum" or vs.type == "reference" then
447 if instanceof(class, AbstractValue) then
448 local obj = class(self.map, self, option, ...)
450 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
453 self.fields[option] = obj
455 elseif class == true then
456 error("No valid class was given and autodetection failed.")
458 error("class must be a descendant of AbstractValue")
462 -- Parse optional options
463 function AbstractSection.parse_optionals(self, section)
464 if not self.optional then
468 self.optionals[section] = {}
470 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
471 for k,v in ipairs(self.children) do
472 if v.optional and not v:cfgvalue(section) then
473 if field == v.option then
476 table.insert(self.optionals[section], v)
481 if field and #field > 0 and self.dynamic then
482 self:add_dynamic(field)
486 -- Add a dynamic option
487 function AbstractSection.add_dynamic(self, field, optional)
488 local o = self:option(Value, field, field)
489 o.optional = optional
492 -- Parse all dynamic options
493 function AbstractSection.parse_dynamic(self, section)
494 if not self.dynamic then
498 local arr = luci.util.clone(self:cfgvalue(section))
499 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
500 for k, v in pairs(form) do
504 for key,val in pairs(arr) do
507 for i,c in ipairs(self.children) do
508 if c.option == key then
513 if create and key:sub(1, 1) ~= "." then
514 self:add_dynamic(key, true)
519 -- Returns the section's UCI table
520 function AbstractSection.cfgvalue(self, section)
521 return self.map:get(section)
524 -- Removes the section
525 function AbstractSection.remove(self, section)
526 return self.map:del(section)
529 -- Creates the section
530 function AbstractSection.create(self, section)
534 stat = self.map:set(section, nil, self.sectiontype)
536 section = self.map:add(self.sectiontype)
541 for k,v in pairs(self.children) do
543 self.map:set(section, v.option, v.default)
547 for k,v in pairs(self.defaults) do
548 self.map:set(section, k, v)
556 SimpleSection = class(AbstractSection)
558 function SimpleSection.__init__(self, form, ...)
559 AbstractSection.__init__(self, form, nil, ...)
560 self.template = "cbi/nullsection"
564 Table = class(AbstractSection)
566 function Table.__init__(self, form, data, ...)
567 local datasource = {}
568 datasource.config = "table"
571 function datasource.get(self, section, option)
572 return data[section] and data[section][option]
575 function datasource.del(...)
579 function datasource.get_scheme()
583 AbstractSection.__init__(self, datasource, "table", ...)
584 self.template = "cbi/tblsection"
585 self.rowcolors = true
586 self.anonymous = true
589 function Table.parse(self)
590 for i, k in ipairs(self:cfgsections()) do
591 if luci.http.formvalue("cbi.submit") then
597 function Table.cfgsections(self)
600 for i, v in luci.util.kspairs(self.data) do
601 table.insert(sections, i)
610 NamedSection - A fixed configuration section defined by its name
612 NamedSection = class(AbstractSection)
614 function NamedSection.__init__(self, map, section, stype, ...)
615 AbstractSection.__init__(self, map, stype, ...)
616 Node._i18n(self, map.config, section, nil, ...)
619 self.addremove = false
621 -- Use defaults from UVL
622 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
623 local vs = self.map:get_scheme(self.sectiontype)
624 self.addremove = not vs.unique and not vs.required
625 self.dynamic = vs.dynamic
626 self.title = self.title or vs.title
627 self.description = self.description or vs.descr
630 self.template = "cbi/nsection"
631 self.section = section
634 function NamedSection.parse(self)
635 local s = self.section
636 local active = self:cfgvalue(s)
638 if self.addremove then
639 local path = self.config.."."..s
640 if active then -- Remove the section
641 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
644 else -- Create and apply default values
645 if luci.http.formvalue("cbi.cns."..path) then
653 AbstractSection.parse_dynamic(self, s)
654 if luci.http.formvalue("cbi.submit") then
657 if not self.override_scheme and self.map.scheme then
658 _uvl_validate_section(self, s)
661 AbstractSection.parse_optionals(self, s)
667 TypedSection - A (set of) configuration section(s) defined by the type
668 addremove: Defines whether the user can add/remove sections of this type
669 anonymous: Allow creating anonymous sections
670 validate: a validation function returning nil if the section is invalid
672 TypedSection = class(AbstractSection)
674 function TypedSection.__init__(self, map, type, ...)
675 AbstractSection.__init__(self, map, type, ...)
676 Node._i18n(self, map.config, type, nil, ...)
678 self.template = "cbi/tsection"
680 self.anonymous = false
682 -- Use defaults from UVL
683 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
684 local vs = self.map:get_scheme(self.sectiontype)
685 self.addremove = not vs.unique and not vs.required
686 self.dynamic = vs.dynamic
687 self.anonymous = not vs.named
688 self.title = self.title or vs.title
689 self.description = self.description or vs.descr
693 -- Return all matching UCI sections for this TypedSection
694 function TypedSection.cfgsections(self)
696 self.map.uci:foreach(self.map.config, self.sectiontype,
698 if self:checkscope(section[".name"]) then
699 table.insert(sections, section[".name"])
706 -- Limits scope to sections that have certain option => value pairs
707 function TypedSection.depends(self, option, value)
708 table.insert(self.deps, {option=option, value=value})
711 function TypedSection.parse(self)
712 if self.addremove then
714 local crval = REMOVE_PREFIX .. self.config
715 local name = luci.http.formvaluetable(crval)
716 for k,v in pairs(name) do
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 not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) then
1219 table.insert(valid, v)
1228 TextValue - A multi-line value
1231 TextValue = class(AbstractValue)
1233 function TextValue.__init__(self, ...)
1234 AbstractValue.__init__(self, ...)
1235 self.template = "cbi/tvalue"
1241 Button = class(AbstractValue)
1243 function Button.__init__(self, ...)
1244 AbstractValue.__init__(self, ...)
1245 self.template = "cbi/button"
1246 self.inputstyle = nil