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 uci = require("luci.model.uci")
36 local class = luci.util.class
37 local instanceof = luci.util.instanceof
46 CREATE_PREFIX = "cbi.cts."
47 REMOVE_PREFIX = "cbi.rts."
49 -- Loads a CBI map from given file, creating an environment and returns it
50 function load(cbimap, ...)
52 local i18n = require "luci.i18n"
53 require("luci.config")
56 local upldir = "/lib/uci/upload/"
57 local cbidir = luci.util.libpath() .. "/model/cbi/"
58 local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
61 luci.i18n.loadc("cbi")
62 luci.i18n.loadc("uvl")
65 translate=i18n.translate,
66 translatef=i18n.translatef,
70 setfenv(func, setmetatable(env, {__index =
72 return rawget(tbl, key) or _M[key] or _G[key]
75 local maps = { func() }
77 local has_upload = false
79 for i, map in ipairs(maps) do
80 if not instanceof(map, Node) then
81 error("CBI map returns no valid map object!")
85 if map.upload_fields then
87 for _, field in ipairs(map.upload_fields) do
89 field.config .. '.' ..
90 field.section.sectiontype .. '.' ..
99 local uci = luci.model.uci.cursor()
100 local prm = luci.http.context.request.message.params
103 luci.http.setfilehandler(
104 function( field, chunk, eof )
105 if not field then return end
106 if field.name and not cbid then
107 local c, s, o = field.name:gmatch(
108 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
111 if c and s and o then
112 local t = uci:get( c, s )
113 if t and uploads[c.."."..t.."."..o] then
114 local path = upldir .. field.name
115 fd = io.open(path, "w")
124 if field.name == cbid and fd then
140 local function _uvl_validate_section(node, name)
141 local co = node.map:get()
143 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
144 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
146 local function tag_fields(e)
147 if e.option and node.fields[e.option] then
148 if node.fields[e.option].error then
149 node.fields[e.option].error[name] = e
151 node.fields[e.option].error = { [name] = e }
154 for _, c in ipairs(e.childs) do tag_fields(c) end
158 local function tag_section(e)
160 for _, c in ipairs(e.childs) do
161 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
162 table.insert( s, c.childs[1]:string() )
164 table.insert( s, c:string() )
171 node.error = { [name] = s }
176 local stat, err = node.map.validator:validate_section(node.config, name, co)
178 node.map.save = false
185 local function _uvl_strip_remote_dependencies(deps)
188 for k, v in pairs(deps) do
189 k = k:gsub("%$config%.%$section%.", "")
190 if k:match("^[%w_]+$") and type(v) == "string" then
199 -- Node pseudo abstract class
202 function Node.__init__(self, title, description)
204 self.title = title or ""
205 self.description = description or ""
206 self.template = "cbi/node"
210 function Node._i18n(self, config, section, option, title, description)
213 if type(luci.i18n) == "table" then
215 local key = config and config:gsub("[^%w]+", "") or ""
217 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
218 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
220 self.title = title or luci.i18n.translate( key, option or section or config )
221 self.description = description or luci.i18n.translate( key .. "_desc", "" )
226 function Node.prepare(self, ...)
227 for k, child in ipairs(self.children) do
232 -- Append child nodes
233 function Node.append(self, obj)
234 table.insert(self.children, obj)
237 -- Parse this node and its children
238 function Node.parse(self, ...)
239 for k, child in ipairs(self.children) do
245 function Node.render(self, scope)
249 luci.template.render(self.template, scope)
252 -- Render the children
253 function Node.render_children(self, ...)
254 for k, node in ipairs(self.children) do
261 A simple template element
263 Template = class(Node)
265 function Template.__init__(self, template)
267 self.template = template
270 function Template.render(self)
271 luci.template.render(self.template, {self=self})
276 Map - A map describing a configuration file
280 function Map.__init__(self, config, ...)
281 Node.__init__(self, ...)
282 Node._i18n(self, config, nil, nil, ...)
285 self.parsechain = {self.config}
286 self.template = "cbi/map"
287 self.apply_on_parse = nil
288 self.uci = uci.cursor()
290 if not self.uci:load(self.config) then
291 error("Unable to read UCI data: " .. self.config)
294 self.validator = luci.uvl.UVL()
295 self.scheme = self.validator:get_scheme(self.config)
299 function Map.get_scheme(self, sectiontype, option)
301 return self.scheme and self.scheme.sections[sectiontype]
303 return self.scheme and self.scheme.variables[sectiontype]
304 and self.scheme.variables[sectiontype][option]
308 function Map.submitstate(self)
309 return luci.http.formvalue("cbi.submit")
312 -- Chain foreign config
313 function Map.chain(self, config)
314 table.insert(self.parsechain, config)
317 -- Use optimized UCI writing
318 function Map.parse(self)
322 for i, config in ipairs(self.parsechain) do
323 self.uci:save(config)
325 if luci.http.formvalue("cbi.apply") then
326 for i, config in ipairs(self.parsechain) do
327 self.uci:commit(config)
329 -- Refresh data because commit changes section names
330 self.uci:load(config)
332 if self.apply_on_parse then
333 self.uci:apply(self.parsechain)
335 self._apply = function()
336 local cmd = self.uci:apply(self.parsechain, true)
342 Node.parse(self, true)
345 for i, config in ipairs(self.parsechain) do
346 self.uci:unload(config)
348 if type(self.commit_handler) == "function" then
349 self:commit_handler(self:submitstate())
353 if self:submitstate() then
355 return self.changed and FORM_CHANGED or FORM_VALID
364 function Map.render(self, ...)
365 Node.render(self, ...)
367 local fp = self._apply()
373 -- Creates a child section
374 function Map.section(self, class, ...)
375 if instanceof(class, AbstractSection) then
376 local obj = class(self, ...)
380 error("class must be a descendent of AbstractSection")
385 function Map.add(self, sectiontype)
386 return self.uci:add(self.config, sectiontype)
390 function Map.set(self, section, option, value)
392 return self.uci:set(self.config, section, option, value)
394 return self.uci:set(self.config, section, value)
399 function Map.del(self, section, option)
401 return self.uci:delete(self.config, section, option)
403 return self.uci:delete(self.config, section)
408 function Map.get(self, section, option)
410 return self.uci:get_all(self.config)
412 return self.uci:get(self.config, section, option)
414 return self.uci:get_all(self.config, section)
424 Page.__init__ = Node.__init__
425 Page.parse = function() end
429 SimpleForm - A Simple non-UCI form
431 SimpleForm = class(Node)
433 function SimpleForm.__init__(self, config, title, description, data)
434 Node.__init__(self, title, description)
436 self.data = data or {}
437 self.template = "cbi/simpleform"
441 function SimpleForm.parse(self, ...)
442 if luci.http.formvalue("cbi.submit") then
443 Node.parse(self, 1, ...)
447 for k, j in ipairs(self.children) do
448 for i, v in ipairs(j.children) do
450 and (not v.tag_missing or not v.tag_missing[1])
451 and (not v.tag_invalid or not v.tag_invalid[1])
456 not self:submitstate() and FORM_NODATA
457 or valid and FORM_VALID
460 self.dorender = not self.handle or self:handle(state, self.data) ~= false
464 function SimpleForm.render(self, ...)
465 if self.dorender then
466 Node.render(self, ...)
470 function SimpleForm.submitstate(self)
471 return luci.http.formvalue("cbi.submit")
474 function SimpleForm.section(self, class, ...)
475 if instanceof(class, AbstractSection) then
476 local obj = class(self, ...)
480 error("class must be a descendent of AbstractSection")
484 -- Creates a child field
485 function SimpleForm.field(self, class, ...)
487 for k, v in ipairs(self.children) do
488 if instanceof(v, SimpleSection) then
494 section = self:section(SimpleSection)
497 if instanceof(class, AbstractValue) then
498 local obj = class(self, section, ...)
499 obj.track_missing = true
503 error("class must be a descendent of AbstractValue")
507 function SimpleForm.set(self, section, option, value)
508 self.data[option] = value
512 function SimpleForm.del(self, section, option)
513 self.data[option] = nil
517 function SimpleForm.get(self, section, option)
518 return self.data[option]
522 function SimpleForm.get_scheme()
531 AbstractSection = class(Node)
533 function AbstractSection.__init__(self, map, sectiontype, ...)
534 Node.__init__(self, ...)
535 self.sectiontype = sectiontype
537 self.config = map.config
542 self.tag_invalid = {}
543 self.tag_deperror = {}
546 self.addremove = false
550 -- Appends a new option
551 function AbstractSection.option(self, class, option, ...)
552 -- Autodetect from UVL
553 if class == true and self.map:get_scheme(self.sectiontype, option) then
554 local vs = self.map:get_scheme(self.sectiontype, option)
555 if vs.type == "boolean" then
557 elseif vs.type == "list" then
559 elseif vs.type == "enum" or vs.type == "reference" then
566 if instanceof(class, AbstractValue) then
567 local obj = class(self.map, self, option, ...)
569 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
572 self.fields[option] = obj
574 elseif class == true then
575 error("No valid class was given and autodetection failed.")
577 error("class must be a descendant of AbstractValue")
581 -- Parse optional options
582 function AbstractSection.parse_optionals(self, section)
583 if not self.optional then
587 self.optionals[section] = {}
589 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
590 for k,v in ipairs(self.children) do
591 if v.optional and not v:cfgvalue(section) then
592 if field == v.option then
595 table.insert(self.optionals[section], v)
600 if field and #field > 0 and self.dynamic then
601 self:add_dynamic(field)
605 -- Add a dynamic option
606 function AbstractSection.add_dynamic(self, field, optional)
607 local o = self:option(Value, field, field)
608 o.optional = optional
611 -- Parse all dynamic options
612 function AbstractSection.parse_dynamic(self, section)
613 if not self.dynamic then
617 local arr = luci.util.clone(self:cfgvalue(section))
618 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
619 for k, v in pairs(form) do
623 for key,val in pairs(arr) do
626 for i,c in ipairs(self.children) do
627 if c.option == key then
632 if create and key:sub(1, 1) ~= "." then
633 self:add_dynamic(key, true)
638 -- Returns the section's UCI table
639 function AbstractSection.cfgvalue(self, section)
640 return self.map:get(section)
643 -- Removes the section
644 function AbstractSection.remove(self, section)
645 return self.map:del(section)
648 -- Creates the section
649 function AbstractSection.create(self, section)
653 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
655 section = self.map:add(self.sectiontype)
660 for k,v in pairs(self.children) do
662 self.map:set(section, v.option, v.default)
666 for k,v in pairs(self.defaults) do
667 self.map:set(section, k, v)
675 SimpleSection = class(AbstractSection)
677 function SimpleSection.__init__(self, form, ...)
678 AbstractSection.__init__(self, form, nil, ...)
679 self.template = "cbi/nullsection"
683 Table = class(AbstractSection)
685 function Table.__init__(self, form, data, ...)
686 local datasource = {}
687 datasource.config = "table"
690 function datasource.get(self, section, option)
691 return data[section] and data[section][option]
694 function datasource.del(...)
698 function datasource.get_scheme()
702 AbstractSection.__init__(self, datasource, "table", ...)
703 self.template = "cbi/tblsection"
704 self.rowcolors = true
705 self.anonymous = true
708 function Table.parse(self)
709 for i, k in ipairs(self:cfgsections()) do
710 if self.map:submitstate() then
716 function Table.cfgsections(self)
719 for i, v in luci.util.kspairs(self.data) do
720 table.insert(sections, i)
729 NamedSection - A fixed configuration section defined by its name
731 NamedSection = class(AbstractSection)
733 function NamedSection.__init__(self, map, section, stype, ...)
734 AbstractSection.__init__(self, map, stype, ...)
735 Node._i18n(self, map.config, section, nil, ...)
738 self.addremove = false
740 -- Use defaults from UVL
741 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
742 local vs = self.map:get_scheme(self.sectiontype)
743 self.addremove = not vs.unique and not vs.required
744 self.dynamic = vs.dynamic
745 self.title = self.title or vs.title
746 self.description = self.description or vs.descr
749 self.template = "cbi/nsection"
750 self.section = section
753 function NamedSection.parse(self, novld)
754 local s = self.section
755 local active = self:cfgvalue(s)
757 if self.addremove then
758 local path = self.config.."."..s
759 if active then -- Remove the section
760 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
763 else -- Create and apply default values
764 if luci.http.formvalue("cbi.cns."..path) then
772 AbstractSection.parse_dynamic(self, s)
773 if self.map:submitstate() then
776 if not novld and not self.override_scheme and self.map.scheme then
777 _uvl_validate_section(self, s)
780 AbstractSection.parse_optionals(self, s)
786 TypedSection - A (set of) configuration section(s) defined by the type
787 addremove: Defines whether the user can add/remove sections of this type
788 anonymous: Allow creating anonymous sections
789 validate: a validation function returning nil if the section is invalid
791 TypedSection = class(AbstractSection)
793 function TypedSection.__init__(self, map, type, ...)
794 AbstractSection.__init__(self, map, type, ...)
795 Node._i18n(self, map.config, type, nil, ...)
797 self.template = "cbi/tsection"
799 self.anonymous = false
801 -- Use defaults from UVL
802 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
803 local vs = self.map:get_scheme(self.sectiontype)
804 self.addremove = not vs.unique and not vs.required
805 self.dynamic = vs.dynamic
806 self.anonymous = not vs.named
807 self.title = self.title or vs.title
808 self.description = self.description or vs.descr
812 -- Return all matching UCI sections for this TypedSection
813 function TypedSection.cfgsections(self)
815 self.map.uci:foreach(self.map.config, self.sectiontype,
817 if self:checkscope(section[".name"]) then
818 table.insert(sections, section[".name"])
825 -- Limits scope to sections that have certain option => value pairs
826 function TypedSection.depends(self, option, value)
827 table.insert(self.deps, {option=option, value=value})
830 function TypedSection.parse(self, novld)
831 if self.addremove then
833 local crval = REMOVE_PREFIX .. self.config
834 local name = luci.http.formvaluetable(crval)
835 for k,v in pairs(name) do
836 if k:sub(-2) == ".x" then
839 if self:cfgvalue(k) and self:checkscope(k) then
846 for i, k in ipairs(self:cfgsections()) do
847 AbstractSection.parse_dynamic(self, k)
848 if self.map:submitstate() then
851 if not novld and not self.override_scheme and self.map.scheme then
852 _uvl_validate_section(self, k)
855 AbstractSection.parse_optionals(self, k)
858 if self.addremove then
861 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
862 local name = luci.http.formvalue(crval)
863 if self.anonymous then
865 created = self:create()
869 -- Ignore if it already exists
870 if self:cfgvalue(name) then
874 name = self:checkscope(name)
877 self.err_invalid = true
880 if name and #name > 0 then
881 created = self:create(name) and name
883 self.invalid_cts = true
890 AbstractSection.parse_optionals(self, created)
895 -- Verifies scope of sections
896 function TypedSection.checkscope(self, section)
897 -- Check if we are not excluded
898 if self.filter and not self:filter(section) then
902 -- Check if at least one dependency is met
903 if #self.deps > 0 and self:cfgvalue(section) then
906 for k, v in ipairs(self.deps) do
907 if self:cfgvalue(section)[v.option] == v.value then
917 return self:validate(section)
921 -- Dummy validate function
922 function TypedSection.validate(self, section)
928 AbstractValue - An abstract Value Type
929 null: Value can be empty
930 valid: A function returning the value if it is valid otherwise nil
931 depends: A table of option => value pairs of which one must be true
932 default: The default value
933 size: The size of the input fields
934 rmempty: Unset value if empty
935 optional: This value is optional (see AbstractSection.optionals)
937 AbstractValue = class(Node)
939 function AbstractValue.__init__(self, map, section, option, ...)
940 Node.__init__(self, ...)
941 self.section = section
944 self.config = map.config
945 self.tag_invalid = {}
946 self.tag_missing = {}
947 self.tag_reqerror = {}
950 --self.cast = "string"
952 self.track_missing = false
953 --self.rmempty = false
956 self.optional = false
959 function AbstractValue.prepare(self)
960 -- Use defaults from UVL
961 if not self.override_scheme
962 and self.map:get_scheme(self.section.sectiontype, self.option) then
963 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
964 if self.rmempty == nil then
965 self.rmempty = not vs.required
967 if self.cast == nil then
968 self.cast = (vs.type == "list") and "list" or "string"
970 self.title = self.title or vs.title
971 self.description = self.description or vs.descr
972 if self.default == nil then
973 self.default = vs.default
976 if vs.depends and not self.override_dependencies then
977 for i, deps in ipairs(vs.depends) do
978 deps = _uvl_strip_remote_dependencies(deps)
986 self.cast = self.cast or "string"
989 -- Add a dependencie to another section field
990 function AbstractValue.depends(self, field, value)
992 if type(field) == "string" then
999 table.insert(self.deps, {deps=deps, add=""})
1002 -- Generates the unique CBID
1003 function AbstractValue.cbid(self, section)
1004 return "cbid."..self.map.config.."."..section.."."..self.option
1007 -- Return whether this object should be created
1008 function AbstractValue.formcreated(self, section)
1009 local key = "cbi.opt."..self.config.."."..section
1010 return (luci.http.formvalue(key) == self.option)
1013 -- Returns the formvalue for this object
1014 function AbstractValue.formvalue(self, section)
1015 return luci.http.formvalue(self:cbid(section))
1018 function AbstractValue.additional(self, value)
1019 self.optional = value
1022 function AbstractValue.mandatory(self, value)
1023 self.rmempty = not value
1026 function AbstractValue.parse(self, section)
1027 local fvalue = self:formvalue(section)
1028 local cvalue = self:cfgvalue(section)
1030 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1031 fvalue = self:transform(self:validate(fvalue, section))
1033 self.tag_invalid[section] = true
1035 if fvalue and not (fvalue == cvalue) then
1036 self:write(section, fvalue)
1038 else -- Unset the UCI or error
1039 if self.rmempty or self.optional then
1040 self:remove(section)
1041 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
1042 self.tag_missing[section] = true
1047 -- Render if this value exists or if it is mandatory
1048 function AbstractValue.render(self, s, scope)
1049 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1052 scope.cbid = self:cbid(s)
1053 scope.striptags = luci.util.striptags
1055 scope.ifattr = function(cond,key,val)
1057 return string.format(
1058 ' %s="%s"', tostring(key),
1059 luci.util.pcdata(tostring( val
1061 or (type(self[key]) ~= "function" and self[key])
1069 scope.attr = function(...)
1070 return scope.ifattr( true, ... )
1073 Node.render(self, scope)
1077 -- Return the UCI value of this object
1078 function AbstractValue.cfgvalue(self, section)
1079 local value = self.map:get(section, self.option)
1082 elseif not self.cast or self.cast == type(value) then
1084 elseif self.cast == "string" then
1085 if type(value) == "table" then
1088 elseif self.cast == "table" then
1089 return luci.util.split(value, "%s+", nil, true)
1093 -- Validate the form value
1094 function AbstractValue.validate(self, value)
1098 AbstractValue.transform = AbstractValue.validate
1102 function AbstractValue.write(self, section, value)
1103 return self.map:set(section, self.option, value)
1107 function AbstractValue.remove(self, section)
1108 return self.map:del(section, self.option)
1115 Value - A one-line value
1116 maxlength: The maximum length
1118 Value = class(AbstractValue)
1120 function Value.__init__(self, ...)
1121 AbstractValue.__init__(self, ...)
1122 self.template = "cbi/value"
1127 function Value.value(self, key, val)
1129 table.insert(self.keylist, tostring(key))
1130 table.insert(self.vallist, tostring(val))
1134 -- DummyValue - This does nothing except being there
1135 DummyValue = class(AbstractValue)
1137 function DummyValue.__init__(self, ...)
1138 AbstractValue.__init__(self, ...)
1139 self.template = "cbi/dvalue"
1143 function DummyValue.cfgvalue(self, section)
1146 if type(self.value) == "function" then
1147 value = self:value(section)
1152 value = AbstractValue.cfgvalue(self, section)
1157 function DummyValue.parse(self)
1163 Flag - A flag being enabled or disabled
1165 Flag = class(AbstractValue)
1167 function Flag.__init__(self, ...)
1168 AbstractValue.__init__(self, ...)
1169 self.template = "cbi/fvalue"
1175 -- A flag can only have two states: set or unset
1176 function Flag.parse(self, section)
1177 local fvalue = self:formvalue(section)
1180 fvalue = self.enabled
1182 fvalue = self.disabled
1185 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1186 if not(fvalue == self:cfgvalue(section)) then
1187 self:write(section, fvalue)
1190 self:remove(section)
1197 ListValue - A one-line value predefined in a list
1198 widget: The widget that will be used (select, radio)
1200 ListValue = class(AbstractValue)
1202 function ListValue.__init__(self, ...)
1203 AbstractValue.__init__(self, ...)
1204 self.template = "cbi/lvalue"
1209 self.widget = "select"
1212 function ListValue.prepare(self, ...)
1213 AbstractValue.prepare(self, ...)
1214 if not self.override_scheme
1215 and self.map:get_scheme(self.section.sectiontype, self.option) then
1216 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1217 if self.value and vs.valuelist and not self.override_values then
1218 for k, v in ipairs(vs.valuelist) do
1220 if not self.override_dependencies
1221 and vs.enum_depends and vs.enum_depends[v.value] then
1222 for i, dep in ipairs(vs.enum_depends[v.value]) do
1223 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1226 self:value(v.value, v.title or v.value, unpack(deps))
1232 function ListValue.value(self, key, val, ...)
1233 if luci.util.contains(self.keylist, key) then
1238 table.insert(self.keylist, tostring(key))
1239 table.insert(self.vallist, tostring(val))
1241 for i, deps in ipairs({...}) do
1242 table.insert(self.deps, {add = "-"..key, deps=deps})
1246 function ListValue.validate(self, val)
1247 if luci.util.contains(self.keylist, val) then
1257 MultiValue - Multiple delimited values
1258 widget: The widget that will be used (select, checkbox)
1259 delimiter: The delimiter that will separate the values (default: " ")
1261 MultiValue = class(AbstractValue)
1263 function MultiValue.__init__(self, ...)
1264 AbstractValue.__init__(self, ...)
1265 self.template = "cbi/mvalue"
1270 self.widget = "checkbox"
1271 self.delimiter = " "
1274 function MultiValue.render(self, ...)
1275 if self.widget == "select" and not self.size then
1276 self.size = #self.vallist
1279 AbstractValue.render(self, ...)
1282 function MultiValue.value(self, key, val)
1283 if luci.util.contains(self.keylist, key) then
1288 table.insert(self.keylist, tostring(key))
1289 table.insert(self.vallist, tostring(val))
1292 function MultiValue.valuelist(self, section)
1293 local val = self:cfgvalue(section)
1295 if not(type(val) == "string") then
1299 return luci.util.split(val, self.delimiter)
1302 function MultiValue.validate(self, val)
1303 val = (type(val) == "table") and val or {val}
1307 for i, value in ipairs(val) do
1308 if luci.util.contains(self.keylist, value) then
1309 result = result and (result .. self.delimiter .. value) or value
1317 StaticList = class(MultiValue)
1319 function StaticList.__init__(self, ...)
1320 MultiValue.__init__(self, ...)
1322 self.valuelist = self.cfgvalue
1324 if not self.override_scheme
1325 and self.map:get_scheme(self.section.sectiontype, self.option) then
1326 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1327 if self.value and vs.values and not self.override_values then
1328 for k, v in pairs(vs.values) do
1335 function StaticList.validate(self, value)
1336 value = (type(value) == "table") and value or {value}
1339 for i, v in ipairs(value) do
1340 if luci.util.contains(self.vallist, v) then
1341 table.insert(valid, v)
1348 DynamicList = class(AbstractValue)
1350 function DynamicList.__init__(self, ...)
1351 AbstractValue.__init__(self, ...)
1352 self.template = "cbi/dynlist"
1358 function DynamicList.value(self, key, val)
1360 table.insert(self.keylist, tostring(key))
1361 table.insert(self.vallist, tostring(val))
1364 function DynamicList.formvalue(self, section)
1365 local value = AbstractValue.formvalue(self, section)
1366 value = (type(value) == "table") and value or {value}
1369 for i, v in ipairs(value) do
1371 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1372 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1373 table.insert(valid, v)
1382 TextValue - A multi-line value
1385 TextValue = class(AbstractValue)
1387 function TextValue.__init__(self, ...)
1388 AbstractValue.__init__(self, ...)
1389 self.template = "cbi/tvalue"
1395 Button = class(AbstractValue)
1397 function Button.__init__(self, ...)
1398 AbstractValue.__init__(self, ...)
1399 self.template = "cbi/button"
1400 self.inputstyle = nil
1405 FileUpload = class(AbstractValue)
1407 function FileUpload.__init__(self, ...)
1408 AbstractValue.__init__(self, ...)
1409 self.template = "cbi/upload"
1410 if not self.map.upload_fields then
1411 self.map.upload_fields = { self }
1413 self.map.upload_fields[#self.map.upload_fields+1] = self
1417 function FileUpload.formcreated(self, section)
1418 return AbstractValue.formcreated(self, section) or
1419 luci.http.formvalue("cbi.rlf."..section.."."..self.option) or
1420 luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1423 function FileUpload.cfgvalue(self, section)
1424 local val = AbstractValue.cfgvalue(self, section)
1425 if val and luci.fs.access(val) then
1431 function FileUpload.formvalue(self, section)
1432 local val = AbstractValue.formvalue(self, section)
1434 if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
1435 not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1445 function FileUpload.remove(self, section)
1446 local val = AbstractValue.formvalue(self, section)
1447 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1448 return AbstractValue.remove(self, section)
1452 FileBrowser = class(AbstractValue)
1454 function FileBrowser.__init__(self, ...)
1455 AbstractValue.__init__(self, ...)
1456 self.template = "cbi/browser"