2 LuCI - Configuration Bind Interface
5 Offers an interface for binding configuration values to certain
6 data types. Supports value and range validation and basic dependencies.
12 Copyright 2008 Steven Barth <steven@midlink.org>
14 Licensed under the Apache License, Version 2.0 (the "License");
15 you may not use this file except in compliance with the License.
16 You may obtain a copy of the License at
18 http://www.apache.org/licenses/LICENSE-2.0
20 Unless required by applicable law or agreed to in writing, software
21 distributed under the License is distributed on an "AS IS" BASIS,
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 See the License for the specific language governing permissions and
24 limitations under the License.
27 module("luci.cbi", package.seeall)
29 require("luci.template")
34 local uci = require("luci.model.uci")
35 local class = luci.util.class
36 local instanceof = luci.util.instanceof
44 CREATE_PREFIX = "cbi.cts."
45 REMOVE_PREFIX = "cbi.rts."
47 -- Loads a CBI map from given file, creating an environment and returns it
48 function load(cbimap, ...)
50 local i18n = require "luci.i18n"
51 require("luci.config")
54 local cbidir = luci.util.libpath() .. "/model/cbi/"
55 local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
58 luci.i18n.loadc("cbi")
59 luci.i18n.loadc("uvl")
62 translate=i18n.translate,
63 translatef=i18n.translatef,
67 setfenv(func, setmetatable(env, {__index =
69 return rawget(tbl, key) or _M[key] or _G[key]
74 for i, map in ipairs(maps) do
75 if not instanceof(map, Node) then
76 error("CBI map returns no valid map object!")
84 local function _uvl_validate_section(node, name)
85 local co = node.map:get()
87 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
88 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
90 local function tag_fields(e)
91 if e.option and node.fields[e.option] then
92 node.fields[e.option].error = e
94 for _, c in ipairs(e.childs) do tag_fields(c) end
98 local function tag_section(e)
100 for _, c in ipairs(e.childs) do
101 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
102 table.insert( s, c.childs[1]:string() )
104 table.insert( s, c:string() )
107 if #s > 0 then node.error = s end
110 local stat, err = node.map.validator:validate_section(node.config, name, co)
112 node.map.save = false
119 local function _uvl_strip_remote_dependencies(deps)
122 for k, v in pairs(deps) do
123 k = k:gsub("%$config%.%$section%.", "")
124 if k:match("^[%w_]+$") and type(v) == "string" then
133 -- Node pseudo abstract class
136 function Node.__init__(self, title, description)
138 self.title = title or ""
139 self.description = description or ""
140 self.template = "cbi/node"
144 function Node._i18n(self, config, section, option, title, description)
147 if type(luci.i18n) == "table" then
149 local key = config and config:gsub("[^%w]+", "") or ""
151 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
152 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
154 self.title = title or luci.i18n.translate( key, option or section or config )
155 self.description = description or luci.i18n.translate( key .. "_desc", "" )
159 -- Append child nodes
160 function Node.append(self, obj)
161 table.insert(self.children, obj)
164 -- Parse this node and its children
165 function Node.parse(self, ...)
166 for k, child in ipairs(self.children) do
172 function Node.render(self, scope)
176 luci.template.render(self.template, scope)
179 -- Render the children
180 function Node.render_children(self, ...)
181 for k, node in ipairs(self.children) do
188 A simple template element
190 Template = class(Node)
192 function Template.__init__(self, template)
194 self.template = template
197 function Template.render(self)
198 luci.template.render(self.template, {self=self})
203 Map - A map describing a configuration file
207 function Map.__init__(self, config, ...)
208 Node.__init__(self, ...)
209 Node._i18n(self, config, nil, nil, ...)
212 self.parsechain = {self.config}
213 self.template = "cbi/map"
214 self.uci = uci.cursor()
216 if not self.uci:load(self.config) then
217 error("Unable to read UCI data: " .. self.config)
220 self.validator = luci.uvl.UVL()
221 self.scheme = self.validator:get_scheme(self.config)
225 function Map.get_scheme(self, sectiontype, option)
227 return self.scheme and self.scheme.sections[sectiontype]
229 return self.scheme and self.scheme.variables[sectiontype]
230 and self.scheme.variables[sectiontype][option]
235 -- Chain foreign config
236 function Map.chain(self, config)
237 table.insert(self.parsechain, config)
240 -- Use optimized UCI writing
241 function Map.parse(self, ...)
242 Node.parse(self, ...)
245 for i, config in ipairs(self.parsechain) do
246 self.uci:save(config)
248 if luci.http.formvalue("cbi.apply") then
249 for i, config in ipairs(self.parsechain) do
250 self.uci:commit(config)
252 -- Refresh data because commit changes section names
253 self.uci:load(config)
255 self.uci:apply(self.parsechain)
258 Node.parse(self, ...)
261 for i, config in ipairs(self.parsechain) do
262 self.uci:unload(config)
267 -- Creates a child section
268 function Map.section(self, class, ...)
269 if instanceof(class, AbstractSection) then
270 local obj = class(self, ...)
274 error("class must be a descendent of AbstractSection")
279 function Map.add(self, sectiontype)
280 return self.uci:add(self.config, sectiontype)
284 function Map.set(self, section, option, value)
286 return self.uci:set(self.config, section, option, value)
288 return self.uci:set(self.config, section, value)
293 function Map.del(self, section, option)
295 return self.uci:delete(self.config, section, option)
297 return self.uci:delete(self.config, section)
302 function Map.get(self, section, option)
304 return self.uci:get_all(self.config)
306 return self.uci:get(self.config, section, option)
308 return self.uci:get_all(self.config, section)
318 Page.__init__ = Node.__init__
319 Page.parse = function() end
323 SimpleForm - A Simple non-UCI form
325 SimpleForm = class(Node)
327 function SimpleForm.__init__(self, config, title, description, data)
328 Node.__init__(self, title, description)
330 self.data = data or {}
331 self.template = "cbi/simpleform"
335 function SimpleForm.parse(self, ...)
336 if luci.http.formvalue("cbi.submit") then
337 Node.parse(self, 1, ...)
341 for k, j in ipairs(self.children) do
342 for i, v in ipairs(j.children) do
344 and (not v.tag_missing or not v.tag_missing[1])
345 and (not v.tag_invalid or not v.tag_invalid[1])
350 not luci.http.formvalue("cbi.submit") and 0
354 self.dorender = not self.handle or self:handle(state, self.data) ~= false
357 function SimpleForm.render(self, ...)
358 if self.dorender then
359 Node.render(self, ...)
363 function SimpleForm.section(self, class, ...)
364 if instanceof(class, AbstractSection) then
365 local obj = class(self, ...)
369 error("class must be a descendent of AbstractSection")
373 -- Creates a child field
374 function SimpleForm.field(self, class, ...)
376 for k, v in ipairs(self.children) do
377 if instanceof(v, SimpleSection) then
383 section = self:section(SimpleSection)
386 if instanceof(class, AbstractValue) then
387 local obj = class(self, section, ...)
388 obj.track_missing = true
392 error("class must be a descendent of AbstractValue")
396 function SimpleForm.set(self, section, option, value)
397 self.data[option] = value
401 function SimpleForm.del(self, section, option)
402 self.data[option] = nil
406 function SimpleForm.get(self, section, option)
407 return self.data[option]
411 function SimpleForm.get_scheme()
420 AbstractSection = class(Node)
422 function AbstractSection.__init__(self, map, sectiontype, ...)
423 Node.__init__(self, ...)
424 self.sectiontype = sectiontype
426 self.config = map.config
431 self.tag_invalid = {}
432 self.tag_deperror = {}
435 self.addremove = false
439 -- Appends a new option
440 function AbstractSection.option(self, class, option, ...)
441 -- Autodetect from UVL
442 if class == true and self.map:get_scheme(self.sectiontype, option) then
443 local vs = self.map:get_scheme(self.sectiontype, option)
444 if vs.type == "boolean" then
446 elseif vs.type == "list" then
448 elseif vs.type == "enum" or vs.type == "reference" then
455 if instanceof(class, AbstractValue) then
456 local obj = class(self.map, self, option, ...)
458 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
461 self.fields[option] = obj
463 elseif class == true then
464 error("No valid class was given and autodetection failed.")
466 error("class must be a descendant of AbstractValue")
470 -- Parse optional options
471 function AbstractSection.parse_optionals(self, section)
472 if not self.optional then
476 self.optionals[section] = {}
478 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
479 for k,v in ipairs(self.children) do
480 if v.optional and not v:cfgvalue(section) then
481 if field == v.option then
484 table.insert(self.optionals[section], v)
489 if field and #field > 0 and self.dynamic then
490 self:add_dynamic(field)
494 -- Add a dynamic option
495 function AbstractSection.add_dynamic(self, field, optional)
496 local o = self:option(Value, field, field)
497 o.optional = optional
500 -- Parse all dynamic options
501 function AbstractSection.parse_dynamic(self, section)
502 if not self.dynamic then
506 local arr = luci.util.clone(self:cfgvalue(section))
507 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
508 for k, v in pairs(form) do
512 for key,val in pairs(arr) do
515 for i,c in ipairs(self.children) do
516 if c.option == key then
521 if create and key:sub(1, 1) ~= "." then
522 self:add_dynamic(key, true)
527 -- Returns the section's UCI table
528 function AbstractSection.cfgvalue(self, section)
529 return self.map:get(section)
532 -- Removes the section
533 function AbstractSection.remove(self, section)
534 return self.map:del(section)
537 -- Creates the section
538 function AbstractSection.create(self, section)
542 stat = self.map:set(section, nil, self.sectiontype)
544 section = self.map:add(self.sectiontype)
549 for k,v in pairs(self.children) do
551 self.map:set(section, v.option, v.default)
555 for k,v in pairs(self.defaults) do
556 self.map:set(section, k, v)
564 SimpleSection = class(AbstractSection)
566 function SimpleSection.__init__(self, form, ...)
567 AbstractSection.__init__(self, form, nil, ...)
568 self.template = "cbi/nullsection"
572 Table = class(AbstractSection)
574 function Table.__init__(self, form, data, ...)
575 local datasource = {}
576 datasource.config = "table"
579 function datasource.get(self, section, option)
580 return data[section] and data[section][option]
583 function datasource.del(...)
587 function datasource.get_scheme()
591 AbstractSection.__init__(self, datasource, "table", ...)
592 self.template = "cbi/tblsection"
593 self.rowcolors = true
594 self.anonymous = true
597 function Table.parse(self)
598 for i, k in ipairs(self:cfgsections()) do
599 if luci.http.formvalue("cbi.submit") then
605 function Table.cfgsections(self)
608 for i, v in luci.util.kspairs(self.data) do
609 table.insert(sections, i)
618 NamedSection - A fixed configuration section defined by its name
620 NamedSection = class(AbstractSection)
622 function NamedSection.__init__(self, map, section, stype, ...)
623 AbstractSection.__init__(self, map, stype, ...)
624 Node._i18n(self, map.config, section, nil, ...)
627 self.addremove = false
629 -- Use defaults from UVL
630 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
631 local vs = self.map:get_scheme(self.sectiontype)
632 self.addremove = not vs.unique and not vs.required
633 self.dynamic = vs.dynamic
634 self.title = self.title or vs.title
635 self.description = self.description or vs.descr
638 self.template = "cbi/nsection"
639 self.section = section
642 function NamedSection.parse(self)
643 local s = self.section
644 local active = self:cfgvalue(s)
646 if self.addremove then
647 local path = self.config.."."..s
648 if active then -- Remove the section
649 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
652 else -- Create and apply default values
653 if luci.http.formvalue("cbi.cns."..path) then
661 AbstractSection.parse_dynamic(self, s)
662 if luci.http.formvalue("cbi.submit") then
665 if not self.override_scheme and self.map.scheme then
666 _uvl_validate_section(self, s)
669 AbstractSection.parse_optionals(self, s)
675 TypedSection - A (set of) configuration section(s) defined by the type
676 addremove: Defines whether the user can add/remove sections of this type
677 anonymous: Allow creating anonymous sections
678 validate: a validation function returning nil if the section is invalid
680 TypedSection = class(AbstractSection)
682 function TypedSection.__init__(self, map, type, ...)
683 AbstractSection.__init__(self, map, type, ...)
684 Node._i18n(self, map.config, type, nil, ...)
686 self.template = "cbi/tsection"
688 self.anonymous = false
690 -- Use defaults from UVL
691 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
692 local vs = self.map:get_scheme(self.sectiontype)
693 self.addremove = not vs.unique and not vs.required
694 self.dynamic = vs.dynamic
695 self.anonymous = not vs.named
696 self.title = self.title or vs.title
697 self.description = self.description or vs.descr
701 -- Return all matching UCI sections for this TypedSection
702 function TypedSection.cfgsections(self)
704 self.map.uci:foreach(self.map.config, self.sectiontype,
706 if self:checkscope(section[".name"]) then
707 table.insert(sections, section[".name"])
714 -- Limits scope to sections that have certain option => value pairs
715 function TypedSection.depends(self, option, value)
716 table.insert(self.deps, {option=option, value=value})
719 function TypedSection.parse(self)
720 if self.addremove then
722 local crval = REMOVE_PREFIX .. self.config
723 local name = luci.http.formvaluetable(crval)
724 for k,v in pairs(name) do
725 if k:sub(-2) == ".x" then
728 if self:cfgvalue(k) and self:checkscope(k) then
735 for i, k in ipairs(self:cfgsections()) do
736 AbstractSection.parse_dynamic(self, k)
737 if luci.http.formvalue("cbi.submit") then
740 if not self.override_scheme and self.map.scheme then
741 _uvl_validate_section(self, k)
744 AbstractSection.parse_optionals(self, k)
747 if self.addremove then
750 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
751 local name = luci.http.formvalue(crval)
752 if self.anonymous then
754 created = self:create()
758 -- Ignore if it already exists
759 if self:cfgvalue(name) then
763 name = self:checkscope(name)
766 self.err_invalid = true
769 if name and #name > 0 then
770 created = self:create(name) and name
776 AbstractSection.parse_optionals(self, created)
781 -- Verifies scope of sections
782 function TypedSection.checkscope(self, section)
783 -- Check if we are not excluded
784 if self.filter and not self:filter(section) then
788 -- Check if at least one dependency is met
789 if #self.deps > 0 and self:cfgvalue(section) then
792 for k, v in ipairs(self.deps) do
793 if self:cfgvalue(section)[v.option] == v.value then
803 return self:validate(section)
807 -- Dummy validate function
808 function TypedSection.validate(self, section)
814 AbstractValue - An abstract Value Type
815 null: Value can be empty
816 valid: A function returning the value if it is valid otherwise nil
817 depends: A table of option => value pairs of which one must be true
818 default: The default value
819 size: The size of the input fields
820 rmempty: Unset value if empty
821 optional: This value is optional (see AbstractSection.optionals)
823 AbstractValue = class(Node)
825 function AbstractValue.__init__(self, map, section, option, ...)
826 Node.__init__(self, ...)
827 self.section = section
830 self.config = map.config
831 self.tag_invalid = {}
832 self.tag_missing = {}
833 self.tag_reqerror = {}
838 self.track_missing = false
842 self.optional = false
844 -- Use defaults from UVL
845 if not self.override_scheme
846 and self.map:get_scheme(self.section.sectiontype, self.option) then
847 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
848 self.rmempty = not vs.required
849 self.cast = (vs.type == "list") and "list" or "string"
850 self.title = self.title or vs.title
851 self.description = self.description or vs.descr
852 self.default = vs.default
854 if vs.depends and not self.override_dependencies then
855 for i, deps in ipairs(vs.depends) do
856 deps = _uvl_strip_remote_dependencies(deps)
865 -- Add a dependencie to another section field
866 function AbstractValue.depends(self, field, value)
868 if type(field) == "string" then
875 table.insert(self.deps, {deps=deps, add=""})
878 -- Generates the unique CBID
879 function AbstractValue.cbid(self, section)
880 return "cbid."..self.map.config.."."..section.."."..self.option
883 -- Return whether this object should be created
884 function AbstractValue.formcreated(self, section)
885 local key = "cbi.opt."..self.config.."."..section
886 return (luci.http.formvalue(key) == self.option)
889 -- Returns the formvalue for this object
890 function AbstractValue.formvalue(self, section)
891 return luci.http.formvalue(self:cbid(section))
894 function AbstractValue.additional(self, value)
895 self.optional = value
898 function AbstractValue.mandatory(self, value)
899 self.rmempty = not value
902 function AbstractValue.parse(self, section)
903 local fvalue = self:formvalue(section)
904 local cvalue = self:cfgvalue(section)
906 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
907 fvalue = self:transform(self:validate(fvalue, section))
909 self.tag_invalid[section] = true
911 if fvalue and not (fvalue == cvalue) then
912 self:write(section, fvalue)
914 else -- Unset the UCI or error
915 if self.rmempty or self.optional then
917 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
918 self.tag_missing[section] = true
923 -- Render if this value exists or if it is mandatory
924 function AbstractValue.render(self, s, scope)
925 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
928 scope.cbid = self:cbid(s)
929 scope.striptags = luci.util.striptags
931 scope.ifattr = function(cond,key,val)
933 return string.format(
934 ' %s="%s"', tostring(key),
935 luci.util.pcdata(tostring( val
937 or (type(self[key]) ~= "function" and self[key])
945 scope.attr = function(...)
946 return scope.ifattr( true, ... )
949 Node.render(self, scope)
953 -- Return the UCI value of this object
954 function AbstractValue.cfgvalue(self, section)
955 local value = self.map:get(section, self.option)
956 if not self.cast or self.cast == type(value) then
958 elseif self.cast == "string" then
959 if type(value) == "table" then
962 elseif self.cast == "table" then
967 -- Validate the form value
968 function AbstractValue.validate(self, value)
972 AbstractValue.transform = AbstractValue.validate
976 function AbstractValue.write(self, section, value)
977 return self.map:set(section, self.option, value)
981 function AbstractValue.remove(self, section)
982 return self.map:del(section, self.option)
989 Value - A one-line value
990 maxlength: The maximum length
992 Value = class(AbstractValue)
994 function Value.__init__(self, ...)
995 AbstractValue.__init__(self, ...)
996 self.template = "cbi/value"
1001 function Value.value(self, key, val)
1003 table.insert(self.keylist, tostring(key))
1004 table.insert(self.vallist, tostring(val))
1008 -- DummyValue - This does nothing except being there
1009 DummyValue = class(AbstractValue)
1011 function DummyValue.__init__(self, ...)
1012 AbstractValue.__init__(self, ...)
1013 self.template = "cbi/dvalue"
1017 function DummyValue.parse(self)
1023 Flag - A flag being enabled or disabled
1025 Flag = class(AbstractValue)
1027 function Flag.__init__(self, ...)
1028 AbstractValue.__init__(self, ...)
1029 self.template = "cbi/fvalue"
1035 -- A flag can only have two states: set or unset
1036 function Flag.parse(self, section)
1037 local fvalue = self:formvalue(section)
1040 fvalue = self.enabled
1042 fvalue = self.disabled
1045 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1046 if not(fvalue == self:cfgvalue(section)) then
1047 self:write(section, fvalue)
1050 self:remove(section)
1057 ListValue - A one-line value predefined in a list
1058 widget: The widget that will be used (select, radio)
1060 ListValue = class(AbstractValue)
1062 function ListValue.__init__(self, ...)
1063 AbstractValue.__init__(self, ...)
1064 self.template = "cbi/lvalue"
1069 self.widget = "select"
1071 if not self.override_scheme
1072 and self.map:get_scheme(self.section.sectiontype, self.option) then
1073 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1074 if self.value and vs.values and not self.override_values then
1075 if self.rmempty or self.optional then
1078 for k, v in pairs(vs.values) do
1080 if not self.override_dependencies
1081 and vs.enum_depends and vs.enum_depends[k] then
1082 for i, dep in ipairs(vs.enum_depends[k]) do
1083 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1086 self:value(k, v, unpack(deps))
1092 function ListValue.value(self, key, val, ...)
1093 if luci.util.contains(self.keylist, key) then
1098 table.insert(self.keylist, tostring(key))
1099 table.insert(self.vallist, tostring(val))
1101 for i, deps in ipairs({...}) do
1102 table.insert(self.deps, {add = "-"..key, deps=deps})
1106 function ListValue.validate(self, val)
1107 if luci.util.contains(self.keylist, val) then
1117 MultiValue - Multiple delimited values
1118 widget: The widget that will be used (select, checkbox)
1119 delimiter: The delimiter that will separate the values (default: " ")
1121 MultiValue = class(AbstractValue)
1123 function MultiValue.__init__(self, ...)
1124 AbstractValue.__init__(self, ...)
1125 self.template = "cbi/mvalue"
1130 self.widget = "checkbox"
1131 self.delimiter = " "
1134 function MultiValue.render(self, ...)
1135 if self.widget == "select" and not self.size then
1136 self.size = #self.vallist
1139 AbstractValue.render(self, ...)
1142 function MultiValue.value(self, key, val)
1143 if luci.util.contains(self.keylist, key) then
1148 table.insert(self.keylist, tostring(key))
1149 table.insert(self.vallist, tostring(val))
1152 function MultiValue.valuelist(self, section)
1153 local val = self:cfgvalue(section)
1155 if not(type(val) == "string") then
1159 return luci.util.split(val, self.delimiter)
1162 function MultiValue.validate(self, val)
1163 val = (type(val) == "table") and val or {val}
1167 for i, value in ipairs(val) do
1168 if luci.util.contains(self.keylist, value) then
1169 result = result and (result .. self.delimiter .. value) or value
1177 StaticList = class(MultiValue)
1179 function StaticList.__init__(self, ...)
1180 MultiValue.__init__(self, ...)
1182 self.valuelist = self.cfgvalue
1184 if not self.override_scheme
1185 and self.map:get_scheme(self.section.sectiontype, self.option) then
1186 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1187 if self.value and vs.values and not self.override_values then
1188 for k, v in pairs(vs.values) do
1195 function StaticList.validate(self, value)
1196 value = (type(value) == "table") and value or {value}
1199 for i, v in ipairs(value) do
1200 if luci.util.contains(self.valuelist, v) then
1201 table.insert(valid, v)
1208 DynamicList = class(AbstractValue)
1210 function DynamicList.__init__(self, ...)
1211 AbstractValue.__init__(self, ...)
1212 self.template = "cbi/dynlist"
1218 function DynamicList.value(self, key, val)
1220 table.insert(self.keylist, tostring(key))
1221 table.insert(self.vallist, tostring(val))
1224 function DynamicList.validate(self, value, section)
1225 value = (type(value) == "table") and value or {value}
1228 for i, v in ipairs(value) do
1230 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1231 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1232 table.insert(valid, v)
1241 TextValue - A multi-line value
1244 TextValue = class(AbstractValue)
1246 function TextValue.__init__(self, ...)
1247 AbstractValue.__init__(self, ...)
1248 self.template = "cbi/tvalue"
1254 Button = class(AbstractValue)
1256 function Button.__init__(self, ...)
1257 AbstractValue.__init__(self, ...)
1258 self.template = "cbi/button"
1259 self.inputstyle = nil