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
45 CREATE_PREFIX = "cbi.cts."
46 REMOVE_PREFIX = "cbi.rts."
48 -- Loads a CBI map from given file, creating an environment and returns it
49 function load(cbimap, ...)
51 local i18n = require "luci.i18n"
52 require("luci.config")
55 local upldir = "/lib/uci/upload/"
56 local cbidir = luci.util.libpath() .. "/model/cbi/"
57 local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
60 luci.i18n.loadc("cbi")
61 luci.i18n.loadc("uvl")
64 translate=i18n.translate,
65 translatef=i18n.translatef,
69 setfenv(func, setmetatable(env, {__index =
71 return rawget(tbl, key) or _M[key] or _G[key]
74 local maps = { func() }
76 local has_upload = false
78 for i, map in ipairs(maps) do
79 if not instanceof(map, Node) then
80 error("CBI map returns no valid map object!")
84 if map.upload_fields then
86 for _, field in ipairs(map.upload_fields) do
88 field.config .. '.' ..
89 field.section.sectiontype .. '.' ..
98 local uci = luci.model.uci.cursor()
99 local prm = luci.http.context.request.message.params
102 luci.http.setfilehandler(
103 function( field, chunk, eof )
104 if not field then return end
105 if field.name and not cbid then
106 local c, s, o = field.name:gmatch(
107 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
110 if c and s and o then
111 local t = uci:get( c, s )
112 if t and uploads[c.."."..t.."."..o] then
113 local path = upldir .. field.name
114 fd = io.open(path, "w")
123 if field.name == cbid and fd then
124 io.stderr:write("*** CHUNK:"..tostring(#chunk).." ***\n")
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]
309 -- Chain foreign config
310 function Map.chain(self, config)
311 table.insert(self.parsechain, config)
314 -- Use optimized UCI writing
315 function Map.parse(self)
319 for i, config in ipairs(self.parsechain) do
320 self.uci:save(config)
322 if luci.http.formvalue("cbi.apply") then
323 for i, config in ipairs(self.parsechain) do
324 self.uci:commit(config)
326 -- Refresh data because commit changes section names
327 self.uci:load(config)
329 if self.apply_on_parse then
330 self.uci:apply(self.parsechain)
332 self._apply = function()
333 local cmd = self.uci:apply(self.parsechain, true)
339 Node.parse(self, true)
342 for i, config in ipairs(self.parsechain) do
343 self.uci:unload(config)
345 if type(self.commit_handler) == "function" then
346 self:commit_handler()
351 function Map.render(self, ...)
352 Node.render(self, ...)
354 local fp = self._apply()
360 -- Creates a child section
361 function Map.section(self, class, ...)
362 if instanceof(class, AbstractSection) then
363 local obj = class(self, ...)
367 error("class must be a descendent of AbstractSection")
372 function Map.add(self, sectiontype)
373 return self.uci:add(self.config, sectiontype)
377 function Map.set(self, section, option, value)
379 return self.uci:set(self.config, section, option, value)
381 return self.uci:set(self.config, section, value)
386 function Map.del(self, section, option)
388 return self.uci:delete(self.config, section, option)
390 return self.uci:delete(self.config, section)
395 function Map.get(self, section, option)
397 return self.uci:get_all(self.config)
399 return self.uci:get(self.config, section, option)
401 return self.uci:get_all(self.config, section)
411 Page.__init__ = Node.__init__
412 Page.parse = function() end
416 SimpleForm - A Simple non-UCI form
418 SimpleForm = class(Node)
420 function SimpleForm.__init__(self, config, title, description, data)
421 Node.__init__(self, title, description)
423 self.data = data or {}
424 self.template = "cbi/simpleform"
428 function SimpleForm.parse(self, ...)
429 if luci.http.formvalue("cbi.submit") then
430 Node.parse(self, 1, ...)
434 for k, j in ipairs(self.children) do
435 for i, v in ipairs(j.children) do
437 and (not v.tag_missing or not v.tag_missing[1])
438 and (not v.tag_invalid or not v.tag_invalid[1])
443 not luci.http.formvalue("cbi.submit") and 0
447 self.dorender = not self.handle or self:handle(state, self.data) ~= false
450 function SimpleForm.render(self, ...)
451 if self.dorender then
452 Node.render(self, ...)
456 function SimpleForm.section(self, class, ...)
457 if instanceof(class, AbstractSection) then
458 local obj = class(self, ...)
462 error("class must be a descendent of AbstractSection")
466 -- Creates a child field
467 function SimpleForm.field(self, class, ...)
469 for k, v in ipairs(self.children) do
470 if instanceof(v, SimpleSection) then
476 section = self:section(SimpleSection)
479 if instanceof(class, AbstractValue) then
480 local obj = class(self, section, ...)
481 obj.track_missing = true
485 error("class must be a descendent of AbstractValue")
489 function SimpleForm.set(self, section, option, value)
490 self.data[option] = value
494 function SimpleForm.del(self, section, option)
495 self.data[option] = nil
499 function SimpleForm.get(self, section, option)
500 return self.data[option]
504 function SimpleForm.get_scheme()
513 AbstractSection = class(Node)
515 function AbstractSection.__init__(self, map, sectiontype, ...)
516 Node.__init__(self, ...)
517 self.sectiontype = sectiontype
519 self.config = map.config
524 self.tag_invalid = {}
525 self.tag_deperror = {}
528 self.addremove = false
532 -- Appends a new option
533 function AbstractSection.option(self, class, option, ...)
534 -- Autodetect from UVL
535 if class == true and self.map:get_scheme(self.sectiontype, option) then
536 local vs = self.map:get_scheme(self.sectiontype, option)
537 if vs.type == "boolean" then
539 elseif vs.type == "list" then
541 elseif vs.type == "enum" or vs.type == "reference" then
548 if instanceof(class, AbstractValue) then
549 local obj = class(self.map, self, option, ...)
551 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
554 self.fields[option] = obj
556 elseif class == true then
557 error("No valid class was given and autodetection failed.")
559 error("class must be a descendant of AbstractValue")
563 -- Parse optional options
564 function AbstractSection.parse_optionals(self, section)
565 if not self.optional then
569 self.optionals[section] = {}
571 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
572 for k,v in ipairs(self.children) do
573 if v.optional and not v:cfgvalue(section) then
574 if field == v.option then
577 table.insert(self.optionals[section], v)
582 if field and #field > 0 and self.dynamic then
583 self:add_dynamic(field)
587 -- Add a dynamic option
588 function AbstractSection.add_dynamic(self, field, optional)
589 local o = self:option(Value, field, field)
590 o.optional = optional
593 -- Parse all dynamic options
594 function AbstractSection.parse_dynamic(self, section)
595 if not self.dynamic then
599 local arr = luci.util.clone(self:cfgvalue(section))
600 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
601 for k, v in pairs(form) do
605 for key,val in pairs(arr) do
608 for i,c in ipairs(self.children) do
609 if c.option == key then
614 if create and key:sub(1, 1) ~= "." then
615 self:add_dynamic(key, true)
620 -- Returns the section's UCI table
621 function AbstractSection.cfgvalue(self, section)
622 return self.map:get(section)
625 -- Removes the section
626 function AbstractSection.remove(self, section)
627 return self.map:del(section)
630 -- Creates the section
631 function AbstractSection.create(self, section)
635 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
637 section = self.map:add(self.sectiontype)
642 for k,v in pairs(self.children) do
644 self.map:set(section, v.option, v.default)
648 for k,v in pairs(self.defaults) do
649 self.map:set(section, k, v)
657 SimpleSection = class(AbstractSection)
659 function SimpleSection.__init__(self, form, ...)
660 AbstractSection.__init__(self, form, nil, ...)
661 self.template = "cbi/nullsection"
665 Table = class(AbstractSection)
667 function Table.__init__(self, form, data, ...)
668 local datasource = {}
669 datasource.config = "table"
672 function datasource.get(self, section, option)
673 return data[section] and data[section][option]
676 function datasource.del(...)
680 function datasource.get_scheme()
684 AbstractSection.__init__(self, datasource, "table", ...)
685 self.template = "cbi/tblsection"
686 self.rowcolors = true
687 self.anonymous = true
690 function Table.parse(self)
691 for i, k in ipairs(self:cfgsections()) do
692 if luci.http.formvalue("cbi.submit") then
698 function Table.cfgsections(self)
701 for i, v in luci.util.kspairs(self.data) do
702 table.insert(sections, i)
711 NamedSection - A fixed configuration section defined by its name
713 NamedSection = class(AbstractSection)
715 function NamedSection.__init__(self, map, section, stype, ...)
716 AbstractSection.__init__(self, map, stype, ...)
717 Node._i18n(self, map.config, section, nil, ...)
720 self.addremove = false
722 -- Use defaults from UVL
723 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
724 local vs = self.map:get_scheme(self.sectiontype)
725 self.addremove = not vs.unique and not vs.required
726 self.dynamic = vs.dynamic
727 self.title = self.title or vs.title
728 self.description = self.description or vs.descr
731 self.template = "cbi/nsection"
732 self.section = section
735 function NamedSection.parse(self, novld)
736 local s = self.section
737 local active = self:cfgvalue(s)
739 if self.addremove then
740 local path = self.config.."."..s
741 if active then -- Remove the section
742 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
745 else -- Create and apply default values
746 if luci.http.formvalue("cbi.cns."..path) then
754 AbstractSection.parse_dynamic(self, s)
755 if luci.http.formvalue("cbi.submit") then
758 if not novld and not self.override_scheme and self.map.scheme then
759 _uvl_validate_section(self, s)
762 AbstractSection.parse_optionals(self, s)
768 TypedSection - A (set of) configuration section(s) defined by the type
769 addremove: Defines whether the user can add/remove sections of this type
770 anonymous: Allow creating anonymous sections
771 validate: a validation function returning nil if the section is invalid
773 TypedSection = class(AbstractSection)
775 function TypedSection.__init__(self, map, type, ...)
776 AbstractSection.__init__(self, map, type, ...)
777 Node._i18n(self, map.config, type, nil, ...)
779 self.template = "cbi/tsection"
781 self.anonymous = false
783 -- Use defaults from UVL
784 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
785 local vs = self.map:get_scheme(self.sectiontype)
786 self.addremove = not vs.unique and not vs.required
787 self.dynamic = vs.dynamic
788 self.anonymous = not vs.named
789 self.title = self.title or vs.title
790 self.description = self.description or vs.descr
794 -- Return all matching UCI sections for this TypedSection
795 function TypedSection.cfgsections(self)
797 self.map.uci:foreach(self.map.config, self.sectiontype,
799 if self:checkscope(section[".name"]) then
800 table.insert(sections, section[".name"])
807 -- Limits scope to sections that have certain option => value pairs
808 function TypedSection.depends(self, option, value)
809 table.insert(self.deps, {option=option, value=value})
812 function TypedSection.parse(self, novld)
813 if self.addremove then
815 local crval = REMOVE_PREFIX .. self.config
816 local name = luci.http.formvaluetable(crval)
817 for k,v in pairs(name) do
818 if k:sub(-2) == ".x" then
821 if self:cfgvalue(k) and self:checkscope(k) then
828 for i, k in ipairs(self:cfgsections()) do
829 AbstractSection.parse_dynamic(self, k)
830 if luci.http.formvalue("cbi.submit") then
833 if not novld and not self.override_scheme and self.map.scheme then
834 _uvl_validate_section(self, k)
837 AbstractSection.parse_optionals(self, k)
840 if self.addremove then
843 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
844 local name = luci.http.formvalue(crval)
845 if self.anonymous then
847 created = self:create()
851 -- Ignore if it already exists
852 if self:cfgvalue(name) then
856 name = self:checkscope(name)
859 self.err_invalid = true
862 if name and #name > 0 then
863 created = self:create(name) and name
865 self.invalid_cts = true
872 AbstractSection.parse_optionals(self, created)
877 -- Verifies scope of sections
878 function TypedSection.checkscope(self, section)
879 -- Check if we are not excluded
880 if self.filter and not self:filter(section) then
884 -- Check if at least one dependency is met
885 if #self.deps > 0 and self:cfgvalue(section) then
888 for k, v in ipairs(self.deps) do
889 if self:cfgvalue(section)[v.option] == v.value then
899 return self:validate(section)
903 -- Dummy validate function
904 function TypedSection.validate(self, section)
910 AbstractValue - An abstract Value Type
911 null: Value can be empty
912 valid: A function returning the value if it is valid otherwise nil
913 depends: A table of option => value pairs of which one must be true
914 default: The default value
915 size: The size of the input fields
916 rmempty: Unset value if empty
917 optional: This value is optional (see AbstractSection.optionals)
919 AbstractValue = class(Node)
921 function AbstractValue.__init__(self, map, section, option, ...)
922 Node.__init__(self, ...)
923 self.section = section
926 self.config = map.config
927 self.tag_invalid = {}
928 self.tag_missing = {}
929 self.tag_reqerror = {}
932 --self.cast = "string"
934 self.track_missing = false
935 --self.rmempty = false
938 self.optional = false
941 function AbstractValue.prepare(self)
942 -- Use defaults from UVL
943 if not self.override_scheme
944 and self.map:get_scheme(self.section.sectiontype, self.option) then
945 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
946 if self.rmempty == nil then
947 self.rmempty = not vs.required
949 if self.cast == nil then
950 self.cast = (vs.type == "list") and "list" or "string"
952 self.title = self.title or vs.title
953 self.description = self.description or vs.descr
954 if self.default == nil then
955 self.default = vs.default
958 if vs.depends and not self.override_dependencies then
959 for i, deps in ipairs(vs.depends) do
960 deps = _uvl_strip_remote_dependencies(deps)
968 self.cast = self.cast or "string"
971 -- Add a dependencie to another section field
972 function AbstractValue.depends(self, field, value)
974 if type(field) == "string" then
981 table.insert(self.deps, {deps=deps, add=""})
984 -- Generates the unique CBID
985 function AbstractValue.cbid(self, section)
986 return "cbid."..self.map.config.."."..section.."."..self.option
989 -- Return whether this object should be created
990 function AbstractValue.formcreated(self, section)
991 local key = "cbi.opt."..self.config.."."..section
992 return (luci.http.formvalue(key) == self.option)
995 -- Returns the formvalue for this object
996 function AbstractValue.formvalue(self, section)
997 return luci.http.formvalue(self:cbid(section))
1000 function AbstractValue.additional(self, value)
1001 self.optional = value
1004 function AbstractValue.mandatory(self, value)
1005 self.rmempty = not value
1008 function AbstractValue.parse(self, section)
1009 local fvalue = self:formvalue(section)
1010 local cvalue = self:cfgvalue(section)
1012 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1013 fvalue = self:transform(self:validate(fvalue, section))
1015 self.tag_invalid[section] = true
1017 if fvalue and not (fvalue == cvalue) then
1018 self:write(section, fvalue)
1020 else -- Unset the UCI or error
1021 if self.rmempty or self.optional then
1022 self:remove(section)
1023 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
1024 self.tag_missing[section] = true
1029 -- Render if this value exists or if it is mandatory
1030 function AbstractValue.render(self, s, scope)
1031 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1034 scope.cbid = self:cbid(s)
1035 scope.striptags = luci.util.striptags
1037 scope.ifattr = function(cond,key,val)
1039 return string.format(
1040 ' %s="%s"', tostring(key),
1041 luci.util.pcdata(tostring( val
1043 or (type(self[key]) ~= "function" and self[key])
1051 scope.attr = function(...)
1052 return scope.ifattr( true, ... )
1055 Node.render(self, scope)
1059 -- Return the UCI value of this object
1060 function AbstractValue.cfgvalue(self, section)
1061 local value = self.map:get(section, self.option)
1064 elseif not self.cast or self.cast == type(value) then
1066 elseif self.cast == "string" then
1067 if type(value) == "table" then
1070 elseif self.cast == "table" then
1071 return luci.util.split(value, "%s+", nil, true)
1075 -- Validate the form value
1076 function AbstractValue.validate(self, value)
1080 AbstractValue.transform = AbstractValue.validate
1084 function AbstractValue.write(self, section, value)
1085 return self.map:set(section, self.option, value)
1089 function AbstractValue.remove(self, section)
1090 return self.map:del(section, self.option)
1097 Value - A one-line value
1098 maxlength: The maximum length
1100 Value = class(AbstractValue)
1102 function Value.__init__(self, ...)
1103 AbstractValue.__init__(self, ...)
1104 self.template = "cbi/value"
1109 function Value.value(self, key, val)
1111 table.insert(self.keylist, tostring(key))
1112 table.insert(self.vallist, tostring(val))
1116 -- DummyValue - This does nothing except being there
1117 DummyValue = class(AbstractValue)
1119 function DummyValue.__init__(self, ...)
1120 AbstractValue.__init__(self, ...)
1121 self.template = "cbi/dvalue"
1125 function DummyValue.cfgvalue(self, section)
1128 if type(self.value) == "function" then
1129 value = self:value(section)
1134 value = AbstractValue.cfgvalue(self, section)
1139 function DummyValue.parse(self)
1145 Flag - A flag being enabled or disabled
1147 Flag = class(AbstractValue)
1149 function Flag.__init__(self, ...)
1150 AbstractValue.__init__(self, ...)
1151 self.template = "cbi/fvalue"
1157 -- A flag can only have two states: set or unset
1158 function Flag.parse(self, section)
1159 local fvalue = self:formvalue(section)
1162 fvalue = self.enabled
1164 fvalue = self.disabled
1167 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1168 if not(fvalue == self:cfgvalue(section)) then
1169 self:write(section, fvalue)
1172 self:remove(section)
1179 ListValue - A one-line value predefined in a list
1180 widget: The widget that will be used (select, radio)
1182 ListValue = class(AbstractValue)
1184 function ListValue.__init__(self, ...)
1185 AbstractValue.__init__(self, ...)
1186 self.template = "cbi/lvalue"
1191 self.widget = "select"
1194 function ListValue.prepare(self, ...)
1195 AbstractValue.prepare(self, ...)
1196 if not self.override_scheme
1197 and self.map:get_scheme(self.section.sectiontype, self.option) then
1198 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1199 if self.value and vs.valuelist and not self.override_values then
1200 for k, v in ipairs(vs.valuelist) do
1202 if not self.override_dependencies
1203 and vs.enum_depends and vs.enum_depends[v.value] then
1204 for i, dep in ipairs(vs.enum_depends[v.value]) do
1205 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1208 self:value(v.value, v.title or v.value, unpack(deps))
1214 function ListValue.value(self, key, val, ...)
1215 if luci.util.contains(self.keylist, key) then
1220 table.insert(self.keylist, tostring(key))
1221 table.insert(self.vallist, tostring(val))
1223 for i, deps in ipairs({...}) do
1224 table.insert(self.deps, {add = "-"..key, deps=deps})
1228 function ListValue.validate(self, val)
1229 if luci.util.contains(self.keylist, val) then
1239 MultiValue - Multiple delimited values
1240 widget: The widget that will be used (select, checkbox)
1241 delimiter: The delimiter that will separate the values (default: " ")
1243 MultiValue = class(AbstractValue)
1245 function MultiValue.__init__(self, ...)
1246 AbstractValue.__init__(self, ...)
1247 self.template = "cbi/mvalue"
1252 self.widget = "checkbox"
1253 self.delimiter = " "
1256 function MultiValue.render(self, ...)
1257 if self.widget == "select" and not self.size then
1258 self.size = #self.vallist
1261 AbstractValue.render(self, ...)
1264 function MultiValue.value(self, key, val)
1265 if luci.util.contains(self.keylist, key) then
1270 table.insert(self.keylist, tostring(key))
1271 table.insert(self.vallist, tostring(val))
1274 function MultiValue.valuelist(self, section)
1275 local val = self:cfgvalue(section)
1277 if not(type(val) == "string") then
1281 return luci.util.split(val, self.delimiter)
1284 function MultiValue.validate(self, val)
1285 val = (type(val) == "table") and val or {val}
1289 for i, value in ipairs(val) do
1290 if luci.util.contains(self.keylist, value) then
1291 result = result and (result .. self.delimiter .. value) or value
1299 StaticList = class(MultiValue)
1301 function StaticList.__init__(self, ...)
1302 MultiValue.__init__(self, ...)
1304 self.valuelist = self.cfgvalue
1306 if not self.override_scheme
1307 and self.map:get_scheme(self.section.sectiontype, self.option) then
1308 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1309 if self.value and vs.values and not self.override_values then
1310 for k, v in pairs(vs.values) do
1317 function StaticList.validate(self, value)
1318 value = (type(value) == "table") and value or {value}
1321 for i, v in ipairs(value) do
1322 if luci.util.contains(self.vallist, v) then
1323 table.insert(valid, v)
1330 DynamicList = class(AbstractValue)
1332 function DynamicList.__init__(self, ...)
1333 AbstractValue.__init__(self, ...)
1334 self.template = "cbi/dynlist"
1340 function DynamicList.value(self, key, val)
1342 table.insert(self.keylist, tostring(key))
1343 table.insert(self.vallist, tostring(val))
1346 function DynamicList.formvalue(self, section)
1347 local value = AbstractValue.formvalue(self, section)
1348 value = (type(value) == "table") and value or {value}
1351 for i, v in ipairs(value) do
1353 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1354 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1355 table.insert(valid, v)
1364 TextValue - A multi-line value
1367 TextValue = class(AbstractValue)
1369 function TextValue.__init__(self, ...)
1370 AbstractValue.__init__(self, ...)
1371 self.template = "cbi/tvalue"
1377 Button = class(AbstractValue)
1379 function Button.__init__(self, ...)
1380 AbstractValue.__init__(self, ...)
1381 self.template = "cbi/button"
1382 self.inputstyle = nil
1387 FileUpload = class(AbstractValue)
1389 function FileUpload.__init__(self, ...)
1390 AbstractValue.__init__(self, ...)
1391 self.template = "cbi/upload"
1392 if not self.map.upload_fields then
1393 self.map.upload_fields = { self }
1395 self.map.upload_fields[#self.map.upload_fields+1] = self
1399 function FileUpload.cfgvalue(self, section)
1400 local val = AbstractValue.cfgvalue(self, section)
1401 if val and luci.fs.access(val) then
1407 function FileUpload.formvalue(self, section)
1408 local val = AbstractValue.formvalue(self, section)
1410 if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
1411 not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1421 function FileUpload.remove(self, section)
1422 local val = AbstractValue.formvalue(self, section)
1423 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1424 return AbstractValue.remove(self, section)