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")
35 --local event = require "luci.sys.event"
36 local uci = require("luci.model.uci")
37 local class = luci.util.class
38 local instanceof = luci.util.instanceof
48 CREATE_PREFIX = "cbi.cts."
49 REMOVE_PREFIX = "cbi.rts."
51 -- Loads a CBI map from given file, creating an environment and returns it
52 function load(cbimap, ...)
54 local i18n = require "luci.i18n"
55 require("luci.config")
58 local upldir = "/lib/uci/upload/"
59 local cbidir = luci.util.libpath() .. "/model/cbi/"
60 local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
63 luci.i18n.loadc("cbi")
64 luci.i18n.loadc("uvl")
67 translate=i18n.translate,
68 translatef=i18n.translatef,
72 setfenv(func, setmetatable(env, {__index =
74 return rawget(tbl, key) or _M[key] or _G[key]
77 local maps = { func() }
79 local has_upload = false
81 for i, map in ipairs(maps) do
82 if not instanceof(map, Node) then
83 error("CBI map returns no valid map object!")
87 if map.upload_fields then
89 for _, field in ipairs(map.upload_fields) do
91 field.config .. '.' ..
92 field.section.sectiontype .. '.' ..
101 local uci = luci.model.uci.cursor()
102 local prm = luci.http.context.request.message.params
105 luci.http.setfilehandler(
106 function( field, chunk, eof )
107 if not field then return end
108 if field.name and not cbid then
109 local c, s, o = field.name:gmatch(
110 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
113 if c and s and o then
114 local t = uci:get( c, s )
115 if t and uploads[c.."."..t.."."..o] then
116 local path = upldir .. field.name
117 fd = io.open(path, "w")
126 if field.name == cbid and fd then
142 local function _uvl_validate_section(node, name)
143 local co = node.map:get()
145 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
146 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
148 local function tag_fields(e)
149 if e.option and node.fields[e.option] then
150 if node.fields[e.option].error then
151 node.fields[e.option].error[name] = e
153 node.fields[e.option].error = { [name] = e }
156 for _, c in ipairs(e.childs) do tag_fields(c) end
160 local function tag_section(e)
162 for _, c in ipairs(e.childs) do
163 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
164 table.insert( s, c.childs[1]:string() )
166 table.insert( s, c:string() )
173 node.error = { [name] = s }
178 local stat, err = node.map.validator:validate_section(node.config, name, co)
180 node.map.save = false
187 local function _uvl_strip_remote_dependencies(deps)
190 for k, v in pairs(deps) do
191 k = k:gsub("%$config%.%$section%.", "")
192 if k:match("^[%w_]+$") and type(v) == "string" then
201 -- Node pseudo abstract class
204 function Node.__init__(self, title, description)
206 self.title = title or ""
207 self.description = description or ""
208 self.template = "cbi/node"
212 function Node._i18n(self, config, section, option, title, description)
215 if type(luci.i18n) == "table" then
217 local key = config and config:gsub("[^%w]+", "") or ""
219 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
220 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
222 self.title = title or luci.i18n.translate( key, option or section or config )
223 self.description = description or luci.i18n.translate( key .. "_desc", "" )
228 function Node.prepare(self, ...)
229 for k, child in ipairs(self.children) do
234 -- Append child nodes
235 function Node.append(self, obj)
236 table.insert(self.children, obj)
239 -- Parse this node and its children
240 function Node.parse(self, ...)
241 for k, child in ipairs(self.children) do
247 function Node.render(self, scope)
251 luci.template.render(self.template, scope)
254 -- Render the children
255 function Node.render_children(self, ...)
256 for k, node in ipairs(self.children) do
263 A simple template element
265 Template = class(Node)
267 function Template.__init__(self, template)
269 self.template = template
272 function Template.render(self)
273 luci.template.render(self.template, {self=self})
278 Map - A map describing a configuration file
282 function Map.__init__(self, config, ...)
283 Node.__init__(self, ...)
284 Node._i18n(self, config, nil, nil, ...)
287 self.parsechain = {self.config}
288 self.template = "cbi/map"
289 self.apply_on_parse = nil
290 self.uci = uci.cursor()
295 if not self.uci:load(self.config) then
296 error("Unable to read UCI data: " .. self.config)
299 self.validator = luci.uvl.UVL()
300 self.scheme = self.validator:get_scheme(self.config)
304 function Map.get_scheme(self, sectiontype, option)
306 return self.scheme and self.scheme.sections[sectiontype]
308 return self.scheme and self.scheme.variables[sectiontype]
309 and self.scheme.variables[sectiontype][option]
313 function Map.submitstate(self)
314 return luci.http.formvalue("cbi.submit")
317 -- Chain foreign config
318 function Map.chain(self, config)
319 table.insert(self.parsechain, config)
322 -- Use optimized UCI writing
323 function Map.parse(self)
327 for i, config in ipairs(self.parsechain) do
328 self.uci:save(config)
330 if self:submitstate() and (self.autoapply or luci.http.formvalue("cbi.apply")) then
331 for i, config in ipairs(self.parsechain) do
332 self.uci:commit(config)
334 -- Refresh data because commit changes section names
335 self.uci:load(config)
337 if self.apply_on_parse then
338 self.uci:apply(self.parsechain)
340 self._apply = function()
341 local cmd = self.uci:apply(self.parsechain, true)
347 Node.parse(self, true)
350 for i, config in ipairs(self.parsechain) do
351 self.uci:unload(config)
353 if type(self.commit_handler) == "function" then
354 self:commit_handler(self:submitstate())
358 if self:submitstate() then
360 self.state = self.changed and FORM_CHANGED or FORM_VALID
362 self.state = FORM_INVALID
365 self.state = FORM_NODATA
371 function Map.render(self, ...)
372 Node.render(self, ...)
374 local fp = self._apply()
380 -- Creates a child section
381 function Map.section(self, class, ...)
382 if instanceof(class, AbstractSection) then
383 local obj = class(self, ...)
387 error("class must be a descendent of AbstractSection")
392 function Map.add(self, sectiontype)
393 return self.uci:add(self.config, sectiontype)
397 function Map.set(self, section, option, value)
399 return self.uci:set(self.config, section, option, value)
401 return self.uci:set(self.config, section, value)
406 function Map.del(self, section, option)
408 return self.uci:delete(self.config, section, option)
410 return self.uci:delete(self.config, section)
415 function Map.get(self, section, option)
417 return self.uci:get_all(self.config)
419 return self.uci:get(self.config, section, option)
421 return self.uci:get_all(self.config, section)
431 Page.__init__ = Node.__init__
432 Page.parse = function() end
436 SimpleForm - A Simple non-UCI form
438 SimpleForm = class(Node)
440 function SimpleForm.__init__(self, config, title, description, data)
441 Node.__init__(self, title, description)
443 self.data = data or {}
444 self.template = "cbi/simpleform"
446 self.pageaction = false
449 function SimpleForm.parse(self, ...)
450 if luci.http.formvalue("cbi.submit") then
451 Node.parse(self, 1, ...)
455 for k, j in ipairs(self.children) do
456 for i, v in ipairs(j.children) do
458 and (not v.tag_missing or not v.tag_missing[1])
459 and (not v.tag_invalid or not v.tag_invalid[1])
464 not self:submitstate() and FORM_NODATA
465 or valid and FORM_VALID
468 self.dorender = not self.handle or self:handle(state, self.data) ~= false
472 function SimpleForm.render(self, ...)
473 if self.dorender then
474 Node.render(self, ...)
478 function SimpleForm.submitstate(self)
479 return luci.http.formvalue("cbi.submit")
482 function SimpleForm.section(self, class, ...)
483 if instanceof(class, AbstractSection) then
484 local obj = class(self, ...)
488 error("class must be a descendent of AbstractSection")
492 -- Creates a child field
493 function SimpleForm.field(self, class, ...)
495 for k, v in ipairs(self.children) do
496 if instanceof(v, SimpleSection) then
502 section = self:section(SimpleSection)
505 if instanceof(class, AbstractValue) then
506 local obj = class(self, section, ...)
507 obj.track_missing = true
511 error("class must be a descendent of AbstractValue")
515 function SimpleForm.set(self, section, option, value)
516 self.data[option] = value
520 function SimpleForm.del(self, section, option)
521 self.data[option] = nil
525 function SimpleForm.get(self, section, option)
526 return self.data[option]
530 function SimpleForm.get_scheme()
539 AbstractSection = class(Node)
541 function AbstractSection.__init__(self, map, sectiontype, ...)
542 Node.__init__(self, ...)
543 self.sectiontype = sectiontype
545 self.config = map.config
550 self.tag_invalid = {}
551 self.tag_deperror = {}
555 self.addremove = false
559 -- Appends a new option
560 function AbstractSection.option(self, class, option, ...)
561 -- Autodetect from UVL
562 if class == true and self.map:get_scheme(self.sectiontype, option) then
563 local vs = self.map:get_scheme(self.sectiontype, option)
564 if vs.type == "boolean" then
566 elseif vs.type == "list" then
568 elseif vs.type == "enum" or vs.type == "reference" then
575 if instanceof(class, AbstractValue) then
576 local obj = class(self.map, self, option, ...)
578 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
581 self.fields[option] = obj
583 elseif class == true then
584 error("No valid class was given and autodetection failed.")
586 error("class must be a descendant of AbstractValue")
590 -- Parse optional options
591 function AbstractSection.parse_optionals(self, section)
592 if not self.optional then
596 self.optionals[section] = {}
598 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
599 for k,v in ipairs(self.children) do
600 if v.optional and not v:cfgvalue(section) then
601 if field == v.option then
604 table.insert(self.optionals[section], v)
609 if field and #field > 0 and self.dynamic then
610 self:add_dynamic(field)
614 -- Add a dynamic option
615 function AbstractSection.add_dynamic(self, field, optional)
616 local o = self:option(Value, field, field)
617 o.optional = optional
620 -- Parse all dynamic options
621 function AbstractSection.parse_dynamic(self, section)
622 if not self.dynamic then
626 local arr = luci.util.clone(self:cfgvalue(section))
627 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
628 for k, v in pairs(form) do
632 for key,val in pairs(arr) do
635 for i,c in ipairs(self.children) do
636 if c.option == key then
641 if create and key:sub(1, 1) ~= "." then
642 self:add_dynamic(key, true)
647 -- Returns the section's UCI table
648 function AbstractSection.cfgvalue(self, section)
649 return self.map:get(section)
653 function AbstractSection.push_events(self)
654 --luci.util.append(self.map.events, self.events)
655 self.map.changed = true
658 -- Removes the section
659 function AbstractSection.remove(self, section)
660 return self.map:del(section)
663 -- Creates the section
664 function AbstractSection.create(self, section)
668 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
670 section = self.map:add(self.sectiontype)
675 for k,v in pairs(self.children) do
677 self.map:set(section, v.option, v.default)
681 for k,v in pairs(self.defaults) do
682 self.map:set(section, k, v)
690 SimpleSection = class(AbstractSection)
692 function SimpleSection.__init__(self, form, ...)
693 AbstractSection.__init__(self, form, nil, ...)
694 self.template = "cbi/nullsection"
698 Table = class(AbstractSection)
700 function Table.__init__(self, form, data, ...)
701 local datasource = {}
702 datasource.config = "table"
705 function datasource.get(self, section, option)
706 return data[section] and data[section][option]
709 function datasource.submitstate(self)
710 return luci.http.formvalue("cbi.submit")
713 function datasource.del(...)
717 function datasource.get_scheme()
721 AbstractSection.__init__(self, datasource, "table", ...)
722 self.template = "cbi/tblsection"
723 self.rowcolors = true
724 self.anonymous = true
727 function Table.parse(self)
728 for i, k in ipairs(self:cfgsections()) do
729 if self.map:submitstate() then
735 function Table.cfgsections(self)
738 for i, v in luci.util.kspairs(self.data) do
739 table.insert(sections, i)
748 NamedSection - A fixed configuration section defined by its name
750 NamedSection = class(AbstractSection)
752 function NamedSection.__init__(self, map, section, stype, ...)
753 AbstractSection.__init__(self, map, stype, ...)
754 Node._i18n(self, map.config, section, nil, ...)
757 self.addremove = false
759 -- Use defaults from UVL
760 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
761 local vs = self.map:get_scheme(self.sectiontype)
762 self.addremove = not vs.unique and not vs.required
763 self.dynamic = vs.dynamic
764 self.title = self.title or vs.title
765 self.description = self.description or vs.descr
768 self.template = "cbi/nsection"
769 self.section = section
772 function NamedSection.parse(self, novld)
773 local s = self.section
774 local active = self:cfgvalue(s)
776 if self.addremove then
777 local path = self.config.."."..s
778 if active then -- Remove the section
779 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
782 else -- Create and apply default values
783 if luci.http.formvalue("cbi.cns."..path) then
791 AbstractSection.parse_dynamic(self, s)
792 if self.map:submitstate() then
795 if not novld and not self.override_scheme and self.map.scheme then
796 _uvl_validate_section(self, s)
799 AbstractSection.parse_optionals(self, s)
809 TypedSection - A (set of) configuration section(s) defined by the type
810 addremove: Defines whether the user can add/remove sections of this type
811 anonymous: Allow creating anonymous sections
812 validate: a validation function returning nil if the section is invalid
814 TypedSection = class(AbstractSection)
816 function TypedSection.__init__(self, map, type, ...)
817 AbstractSection.__init__(self, map, type, ...)
818 Node._i18n(self, map.config, type, nil, ...)
820 self.template = "cbi/tsection"
822 self.anonymous = false
824 -- Use defaults from UVL
825 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
826 local vs = self.map:get_scheme(self.sectiontype)
827 self.addremove = not vs.unique and not vs.required
828 self.dynamic = vs.dynamic
829 self.anonymous = not vs.named
830 self.title = self.title or vs.title
831 self.description = self.description or vs.descr
835 -- Return all matching UCI sections for this TypedSection
836 function TypedSection.cfgsections(self)
838 self.map.uci:foreach(self.map.config, self.sectiontype,
840 if self:checkscope(section[".name"]) then
841 table.insert(sections, section[".name"])
848 -- Limits scope to sections that have certain option => value pairs
849 function TypedSection.depends(self, option, value)
850 table.insert(self.deps, {option=option, value=value})
853 function TypedSection.parse(self, novld)
854 if self.addremove then
856 local crval = REMOVE_PREFIX .. self.config
857 local name = luci.http.formvaluetable(crval)
858 for k,v in pairs(name) do
859 if k:sub(-2) == ".x" then
862 if self:cfgvalue(k) and self:checkscope(k) then
869 for i, k in ipairs(self:cfgsections()) do
870 AbstractSection.parse_dynamic(self, k)
871 if self.map:submitstate() then
874 if not novld and not self.override_scheme and self.map.scheme then
875 _uvl_validate_section(self, k)
878 AbstractSection.parse_optionals(self, k)
881 if self.addremove then
884 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
885 local name = luci.http.formvalue(crval)
886 if self.anonymous then
888 created = self:create()
892 -- Ignore if it already exists
893 if self:cfgvalue(name) then
897 name = self:checkscope(name)
900 self.err_invalid = true
903 if name and #name > 0 then
904 created = self:create(name) and name
906 self.invalid_cts = true
913 AbstractSection.parse_optionals(self, created)
917 if created or self.changed then
922 -- Verifies scope of sections
923 function TypedSection.checkscope(self, section)
924 -- Check if we are not excluded
925 if self.filter and not self:filter(section) then
929 -- Check if at least one dependency is met
930 if #self.deps > 0 and self:cfgvalue(section) then
933 for k, v in ipairs(self.deps) do
934 if self:cfgvalue(section)[v.option] == v.value then
944 return self:validate(section)
948 -- Dummy validate function
949 function TypedSection.validate(self, section)
955 AbstractValue - An abstract Value Type
956 null: Value can be empty
957 valid: A function returning the value if it is valid otherwise nil
958 depends: A table of option => value pairs of which one must be true
959 default: The default value
960 size: The size of the input fields
961 rmempty: Unset value if empty
962 optional: This value is optional (see AbstractSection.optionals)
964 AbstractValue = class(Node)
966 function AbstractValue.__init__(self, map, section, option, ...)
967 Node.__init__(self, ...)
968 self.section = section
971 self.config = map.config
972 self.tag_invalid = {}
973 self.tag_missing = {}
974 self.tag_reqerror = {}
977 --self.cast = "string"
979 self.track_missing = false
980 --self.rmempty = false
983 self.optional = false
986 function AbstractValue.prepare(self)
987 -- Use defaults from UVL
988 if not self.override_scheme
989 and self.map:get_scheme(self.section.sectiontype, self.option) then
990 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
991 if self.rmempty == nil then
992 self.rmempty = not vs.required
994 if self.cast == nil then
995 self.cast = (vs.type == "list") and "list" or "string"
997 self.title = self.title or vs.title
998 self.description = self.description or vs.descr
999 if self.default == nil then
1000 self.default = vs.default
1003 if vs.depends and not self.override_dependencies then
1004 for i, deps in ipairs(vs.depends) do
1005 deps = _uvl_strip_remote_dependencies(deps)
1013 self.cast = self.cast or "string"
1016 -- Add a dependencie to another section field
1017 function AbstractValue.depends(self, field, value)
1019 if type(field) == "string" then
1026 table.insert(self.deps, {deps=deps, add=""})
1029 -- Generates the unique CBID
1030 function AbstractValue.cbid(self, section)
1031 return "cbid."..self.map.config.."."..section.."."..self.option
1034 -- Return whether this object should be created
1035 function AbstractValue.formcreated(self, section)
1036 local key = "cbi.opt."..self.config.."."..section
1037 return (luci.http.formvalue(key) == self.option)
1040 -- Returns the formvalue for this object
1041 function AbstractValue.formvalue(self, section)
1042 return luci.http.formvalue(self:cbid(section))
1045 function AbstractValue.additional(self, value)
1046 self.optional = value
1049 function AbstractValue.mandatory(self, value)
1050 self.rmempty = not value
1053 function AbstractValue.parse(self, section)
1054 local fvalue = self:formvalue(section)
1055 local cvalue = self:cfgvalue(section)
1057 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1058 fvalue = self:transform(self:validate(fvalue, section))
1060 self.tag_invalid[section] = true
1062 if fvalue and not (fvalue == cvalue) then
1063 if self:write(section, fvalue) then
1065 self.section.changed = true
1066 --luci.util.append(self.map.events, self.events)
1069 else -- Unset the UCI or error
1070 if self.rmempty or self.optional then
1071 if self:remove(section) then
1073 self.section.changed = true
1074 --luci.util.append(self.map.events, self.events)
1076 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
1077 self.tag_missing[section] = true
1082 -- Render if this value exists or if it is mandatory
1083 function AbstractValue.render(self, s, scope)
1084 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1087 scope.cbid = self:cbid(s)
1088 scope.striptags = luci.util.striptags
1090 scope.ifattr = function(cond,key,val)
1092 return string.format(
1093 ' %s="%s"', tostring(key),
1094 luci.util.pcdata(tostring( val
1096 or (type(self[key]) ~= "function" and self[key])
1104 scope.attr = function(...)
1105 return scope.ifattr( true, ... )
1108 Node.render(self, scope)
1112 -- Return the UCI value of this object
1113 function AbstractValue.cfgvalue(self, section)
1114 local value = self.map:get(section, self.option)
1117 elseif not self.cast or self.cast == type(value) then
1119 elseif self.cast == "string" then
1120 if type(value) == "table" then
1123 elseif self.cast == "table" then
1124 return luci.util.split(value, "%s+", nil, true)
1128 -- Validate the form value
1129 function AbstractValue.validate(self, value)
1133 AbstractValue.transform = AbstractValue.validate
1137 function AbstractValue.write(self, section, value)
1138 return self.map:set(section, self.option, value)
1142 function AbstractValue.remove(self, section)
1143 return self.map:del(section, self.option)
1150 Value - A one-line value
1151 maxlength: The maximum length
1153 Value = class(AbstractValue)
1155 function Value.__init__(self, ...)
1156 AbstractValue.__init__(self, ...)
1157 self.template = "cbi/value"
1162 function Value.value(self, key, val)
1164 table.insert(self.keylist, tostring(key))
1165 table.insert(self.vallist, tostring(val))
1169 -- DummyValue - This does nothing except being there
1170 DummyValue = class(AbstractValue)
1172 function DummyValue.__init__(self, ...)
1173 AbstractValue.__init__(self, ...)
1174 self.template = "cbi/dvalue"
1178 function DummyValue.cfgvalue(self, section)
1181 if type(self.value) == "function" then
1182 value = self:value(section)
1187 value = AbstractValue.cfgvalue(self, section)
1192 function DummyValue.parse(self)
1198 Flag - A flag being enabled or disabled
1200 Flag = class(AbstractValue)
1202 function Flag.__init__(self, ...)
1203 AbstractValue.__init__(self, ...)
1204 self.template = "cbi/fvalue"
1210 -- A flag can only have two states: set or unset
1211 function Flag.parse(self, section)
1212 local fvalue = self:formvalue(section)
1215 fvalue = self.enabled
1217 fvalue = self.disabled
1220 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1221 if not(fvalue == self:cfgvalue(section)) then
1222 self:write(section, fvalue)
1225 self:remove(section)
1232 ListValue - A one-line value predefined in a list
1233 widget: The widget that will be used (select, radio)
1235 ListValue = class(AbstractValue)
1237 function ListValue.__init__(self, ...)
1238 AbstractValue.__init__(self, ...)
1239 self.template = "cbi/lvalue"
1244 self.widget = "select"
1247 function ListValue.prepare(self, ...)
1248 AbstractValue.prepare(self, ...)
1249 if not self.override_scheme
1250 and self.map:get_scheme(self.section.sectiontype, self.option) then
1251 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1252 if self.value and vs.valuelist and not self.override_values then
1253 for k, v in ipairs(vs.valuelist) do
1255 if not self.override_dependencies
1256 and vs.enum_depends and vs.enum_depends[v.value] then
1257 for i, dep in ipairs(vs.enum_depends[v.value]) do
1258 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1261 self:value(v.value, v.title or v.value, unpack(deps))
1267 function ListValue.value(self, key, val, ...)
1268 if luci.util.contains(self.keylist, key) then
1273 table.insert(self.keylist, tostring(key))
1274 table.insert(self.vallist, tostring(val))
1276 for i, deps in ipairs({...}) do
1277 table.insert(self.deps, {add = "-"..key, deps=deps})
1281 function ListValue.validate(self, val)
1282 if luci.util.contains(self.keylist, val) then
1292 MultiValue - Multiple delimited values
1293 widget: The widget that will be used (select, checkbox)
1294 delimiter: The delimiter that will separate the values (default: " ")
1296 MultiValue = class(AbstractValue)
1298 function MultiValue.__init__(self, ...)
1299 AbstractValue.__init__(self, ...)
1300 self.template = "cbi/mvalue"
1305 self.widget = "checkbox"
1306 self.delimiter = " "
1309 function MultiValue.render(self, ...)
1310 if self.widget == "select" and not self.size then
1311 self.size = #self.vallist
1314 AbstractValue.render(self, ...)
1317 function MultiValue.value(self, key, val)
1318 if luci.util.contains(self.keylist, key) then
1323 table.insert(self.keylist, tostring(key))
1324 table.insert(self.vallist, tostring(val))
1327 function MultiValue.valuelist(self, section)
1328 local val = self:cfgvalue(section)
1330 if not(type(val) == "string") then
1334 return luci.util.split(val, self.delimiter)
1337 function MultiValue.validate(self, val)
1338 val = (type(val) == "table") and val or {val}
1342 for i, value in ipairs(val) do
1343 if luci.util.contains(self.keylist, value) then
1344 result = result and (result .. self.delimiter .. value) or value
1352 StaticList = class(MultiValue)
1354 function StaticList.__init__(self, ...)
1355 MultiValue.__init__(self, ...)
1357 self.valuelist = self.cfgvalue
1359 if not self.override_scheme
1360 and self.map:get_scheme(self.section.sectiontype, self.option) then
1361 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1362 if self.value and vs.values and not self.override_values then
1363 for k, v in pairs(vs.values) do
1370 function StaticList.validate(self, value)
1371 value = (type(value) == "table") and value or {value}
1374 for i, v in ipairs(value) do
1375 if luci.util.contains(self.vallist, v) then
1376 table.insert(valid, v)
1383 DynamicList = class(AbstractValue)
1385 function DynamicList.__init__(self, ...)
1386 AbstractValue.__init__(self, ...)
1387 self.template = "cbi/dynlist"
1393 function DynamicList.value(self, key, val)
1395 table.insert(self.keylist, tostring(key))
1396 table.insert(self.vallist, tostring(val))
1399 function DynamicList.formvalue(self, section)
1400 local value = AbstractValue.formvalue(self, section)
1401 value = (type(value) == "table") and value or {value}
1404 for i, v in ipairs(value) do
1406 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1407 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1408 table.insert(valid, v)
1417 TextValue - A multi-line value
1420 TextValue = class(AbstractValue)
1422 function TextValue.__init__(self, ...)
1423 AbstractValue.__init__(self, ...)
1424 self.template = "cbi/tvalue"
1430 Button = class(AbstractValue)
1432 function Button.__init__(self, ...)
1433 AbstractValue.__init__(self, ...)
1434 self.template = "cbi/button"
1435 self.inputstyle = nil
1440 FileUpload = class(AbstractValue)
1442 function FileUpload.__init__(self, ...)
1443 AbstractValue.__init__(self, ...)
1444 self.template = "cbi/upload"
1445 if not self.map.upload_fields then
1446 self.map.upload_fields = { self }
1448 self.map.upload_fields[#self.map.upload_fields+1] = self
1452 function FileUpload.formcreated(self, section)
1453 return AbstractValue.formcreated(self, section) or
1454 luci.http.formvalue("cbi.rlf."..section.."."..self.option) or
1455 luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1458 function FileUpload.cfgvalue(self, section)
1459 local val = AbstractValue.cfgvalue(self, section)
1460 if val and luci.fs.access(val) then
1466 function FileUpload.formvalue(self, section)
1467 local val = AbstractValue.formvalue(self, section)
1469 if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
1470 not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1480 function FileUpload.remove(self, section)
1481 local val = AbstractValue.formvalue(self, section)
1482 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1483 return AbstractValue.remove(self, section)
1487 FileBrowser = class(AbstractValue)
1489 function FileBrowser.__init__(self, ...)
1490 AbstractValue.__init__(self, ...)
1491 self.template = "cbi/browser"