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.submitstate(self)
695 return luci.http.formvalue("cbi.submit")
698 function datasource.del(...)
702 function datasource.get_scheme()
706 AbstractSection.__init__(self, datasource, "table", ...)
707 self.template = "cbi/tblsection"
708 self.rowcolors = true
709 self.anonymous = true
712 function Table.parse(self)
713 for i, k in ipairs(self:cfgsections()) do
714 if self.map:submitstate() then
720 function Table.cfgsections(self)
723 for i, v in luci.util.kspairs(self.data) do
724 table.insert(sections, i)
733 NamedSection - A fixed configuration section defined by its name
735 NamedSection = class(AbstractSection)
737 function NamedSection.__init__(self, map, section, stype, ...)
738 AbstractSection.__init__(self, map, stype, ...)
739 Node._i18n(self, map.config, section, nil, ...)
742 self.addremove = false
744 -- Use defaults from UVL
745 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
746 local vs = self.map:get_scheme(self.sectiontype)
747 self.addremove = not vs.unique and not vs.required
748 self.dynamic = vs.dynamic
749 self.title = self.title or vs.title
750 self.description = self.description or vs.descr
753 self.template = "cbi/nsection"
754 self.section = section
757 function NamedSection.parse(self, novld)
758 local s = self.section
759 local active = self:cfgvalue(s)
761 if self.addremove then
762 local path = self.config.."."..s
763 if active then -- Remove the section
764 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
767 else -- Create and apply default values
768 if luci.http.formvalue("cbi.cns."..path) then
776 AbstractSection.parse_dynamic(self, s)
777 if self.map:submitstate() then
780 if not novld and not self.override_scheme and self.map.scheme then
781 _uvl_validate_section(self, s)
784 AbstractSection.parse_optionals(self, s)
790 TypedSection - A (set of) configuration section(s) defined by the type
791 addremove: Defines whether the user can add/remove sections of this type
792 anonymous: Allow creating anonymous sections
793 validate: a validation function returning nil if the section is invalid
795 TypedSection = class(AbstractSection)
797 function TypedSection.__init__(self, map, type, ...)
798 AbstractSection.__init__(self, map, type, ...)
799 Node._i18n(self, map.config, type, nil, ...)
801 self.template = "cbi/tsection"
803 self.anonymous = false
805 -- Use defaults from UVL
806 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
807 local vs = self.map:get_scheme(self.sectiontype)
808 self.addremove = not vs.unique and not vs.required
809 self.dynamic = vs.dynamic
810 self.anonymous = not vs.named
811 self.title = self.title or vs.title
812 self.description = self.description or vs.descr
816 -- Return all matching UCI sections for this TypedSection
817 function TypedSection.cfgsections(self)
819 self.map.uci:foreach(self.map.config, self.sectiontype,
821 if self:checkscope(section[".name"]) then
822 table.insert(sections, section[".name"])
829 -- Limits scope to sections that have certain option => value pairs
830 function TypedSection.depends(self, option, value)
831 table.insert(self.deps, {option=option, value=value})
834 function TypedSection.parse(self, novld)
835 if self.addremove then
837 local crval = REMOVE_PREFIX .. self.config
838 local name = luci.http.formvaluetable(crval)
839 for k,v in pairs(name) do
840 if k:sub(-2) == ".x" then
843 if self:cfgvalue(k) and self:checkscope(k) then
850 for i, k in ipairs(self:cfgsections()) do
851 AbstractSection.parse_dynamic(self, k)
852 if self.map:submitstate() then
855 if not novld and not self.override_scheme and self.map.scheme then
856 _uvl_validate_section(self, k)
859 AbstractSection.parse_optionals(self, k)
862 if self.addremove then
865 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
866 local name = luci.http.formvalue(crval)
867 if self.anonymous then
869 created = self:create()
873 -- Ignore if it already exists
874 if self:cfgvalue(name) then
878 name = self:checkscope(name)
881 self.err_invalid = true
884 if name and #name > 0 then
885 created = self:create(name) and name
887 self.invalid_cts = true
894 AbstractSection.parse_optionals(self, created)
899 -- Verifies scope of sections
900 function TypedSection.checkscope(self, section)
901 -- Check if we are not excluded
902 if self.filter and not self:filter(section) then
906 -- Check if at least one dependency is met
907 if #self.deps > 0 and self:cfgvalue(section) then
910 for k, v in ipairs(self.deps) do
911 if self:cfgvalue(section)[v.option] == v.value then
921 return self:validate(section)
925 -- Dummy validate function
926 function TypedSection.validate(self, section)
932 AbstractValue - An abstract Value Type
933 null: Value can be empty
934 valid: A function returning the value if it is valid otherwise nil
935 depends: A table of option => value pairs of which one must be true
936 default: The default value
937 size: The size of the input fields
938 rmempty: Unset value if empty
939 optional: This value is optional (see AbstractSection.optionals)
941 AbstractValue = class(Node)
943 function AbstractValue.__init__(self, map, section, option, ...)
944 Node.__init__(self, ...)
945 self.section = section
948 self.config = map.config
949 self.tag_invalid = {}
950 self.tag_missing = {}
951 self.tag_reqerror = {}
954 --self.cast = "string"
956 self.track_missing = false
957 --self.rmempty = false
960 self.optional = false
963 function AbstractValue.prepare(self)
964 -- Use defaults from UVL
965 if not self.override_scheme
966 and self.map:get_scheme(self.section.sectiontype, self.option) then
967 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
968 if self.rmempty == nil then
969 self.rmempty = not vs.required
971 if self.cast == nil then
972 self.cast = (vs.type == "list") and "list" or "string"
974 self.title = self.title or vs.title
975 self.description = self.description or vs.descr
976 if self.default == nil then
977 self.default = vs.default
980 if vs.depends and not self.override_dependencies then
981 for i, deps in ipairs(vs.depends) do
982 deps = _uvl_strip_remote_dependencies(deps)
990 self.cast = self.cast or "string"
993 -- Add a dependencie to another section field
994 function AbstractValue.depends(self, field, value)
996 if type(field) == "string" then
1003 table.insert(self.deps, {deps=deps, add=""})
1006 -- Generates the unique CBID
1007 function AbstractValue.cbid(self, section)
1008 return "cbid."..self.map.config.."."..section.."."..self.option
1011 -- Return whether this object should be created
1012 function AbstractValue.formcreated(self, section)
1013 local key = "cbi.opt."..self.config.."."..section
1014 return (luci.http.formvalue(key) == self.option)
1017 -- Returns the formvalue for this object
1018 function AbstractValue.formvalue(self, section)
1019 return luci.http.formvalue(self:cbid(section))
1022 function AbstractValue.additional(self, value)
1023 self.optional = value
1026 function AbstractValue.mandatory(self, value)
1027 self.rmempty = not value
1030 function AbstractValue.parse(self, section)
1031 local fvalue = self:formvalue(section)
1032 local cvalue = self:cfgvalue(section)
1034 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1035 fvalue = self:transform(self:validate(fvalue, section))
1037 self.tag_invalid[section] = true
1039 if fvalue and not (fvalue == cvalue) then
1040 self:write(section, fvalue)
1042 else -- Unset the UCI or error
1043 if self.rmempty or self.optional then
1044 self:remove(section)
1045 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
1046 self.tag_missing[section] = true
1051 -- Render if this value exists or if it is mandatory
1052 function AbstractValue.render(self, s, scope)
1053 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1056 scope.cbid = self:cbid(s)
1057 scope.striptags = luci.util.striptags
1059 scope.ifattr = function(cond,key,val)
1061 return string.format(
1062 ' %s="%s"', tostring(key),
1063 luci.util.pcdata(tostring( val
1065 or (type(self[key]) ~= "function" and self[key])
1073 scope.attr = function(...)
1074 return scope.ifattr( true, ... )
1077 Node.render(self, scope)
1081 -- Return the UCI value of this object
1082 function AbstractValue.cfgvalue(self, section)
1083 local value = self.map:get(section, self.option)
1086 elseif not self.cast or self.cast == type(value) then
1088 elseif self.cast == "string" then
1089 if type(value) == "table" then
1092 elseif self.cast == "table" then
1093 return luci.util.split(value, "%s+", nil, true)
1097 -- Validate the form value
1098 function AbstractValue.validate(self, value)
1102 AbstractValue.transform = AbstractValue.validate
1106 function AbstractValue.write(self, section, value)
1107 return self.map:set(section, self.option, value)
1111 function AbstractValue.remove(self, section)
1112 return self.map:del(section, self.option)
1119 Value - A one-line value
1120 maxlength: The maximum length
1122 Value = class(AbstractValue)
1124 function Value.__init__(self, ...)
1125 AbstractValue.__init__(self, ...)
1126 self.template = "cbi/value"
1131 function Value.value(self, key, val)
1133 table.insert(self.keylist, tostring(key))
1134 table.insert(self.vallist, tostring(val))
1138 -- DummyValue - This does nothing except being there
1139 DummyValue = class(AbstractValue)
1141 function DummyValue.__init__(self, ...)
1142 AbstractValue.__init__(self, ...)
1143 self.template = "cbi/dvalue"
1147 function DummyValue.cfgvalue(self, section)
1150 if type(self.value) == "function" then
1151 value = self:value(section)
1156 value = AbstractValue.cfgvalue(self, section)
1161 function DummyValue.parse(self)
1167 Flag - A flag being enabled or disabled
1169 Flag = class(AbstractValue)
1171 function Flag.__init__(self, ...)
1172 AbstractValue.__init__(self, ...)
1173 self.template = "cbi/fvalue"
1179 -- A flag can only have two states: set or unset
1180 function Flag.parse(self, section)
1181 local fvalue = self:formvalue(section)
1184 fvalue = self.enabled
1186 fvalue = self.disabled
1189 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1190 if not(fvalue == self:cfgvalue(section)) then
1191 self:write(section, fvalue)
1194 self:remove(section)
1201 ListValue - A one-line value predefined in a list
1202 widget: The widget that will be used (select, radio)
1204 ListValue = class(AbstractValue)
1206 function ListValue.__init__(self, ...)
1207 AbstractValue.__init__(self, ...)
1208 self.template = "cbi/lvalue"
1213 self.widget = "select"
1216 function ListValue.prepare(self, ...)
1217 AbstractValue.prepare(self, ...)
1218 if not self.override_scheme
1219 and self.map:get_scheme(self.section.sectiontype, self.option) then
1220 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1221 if self.value and vs.valuelist and not self.override_values then
1222 for k, v in ipairs(vs.valuelist) do
1224 if not self.override_dependencies
1225 and vs.enum_depends and vs.enum_depends[v.value] then
1226 for i, dep in ipairs(vs.enum_depends[v.value]) do
1227 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1230 self:value(v.value, v.title or v.value, unpack(deps))
1236 function ListValue.value(self, key, val, ...)
1237 if luci.util.contains(self.keylist, key) then
1242 table.insert(self.keylist, tostring(key))
1243 table.insert(self.vallist, tostring(val))
1245 for i, deps in ipairs({...}) do
1246 table.insert(self.deps, {add = "-"..key, deps=deps})
1250 function ListValue.validate(self, val)
1251 if luci.util.contains(self.keylist, val) then
1261 MultiValue - Multiple delimited values
1262 widget: The widget that will be used (select, checkbox)
1263 delimiter: The delimiter that will separate the values (default: " ")
1265 MultiValue = class(AbstractValue)
1267 function MultiValue.__init__(self, ...)
1268 AbstractValue.__init__(self, ...)
1269 self.template = "cbi/mvalue"
1274 self.widget = "checkbox"
1275 self.delimiter = " "
1278 function MultiValue.render(self, ...)
1279 if self.widget == "select" and not self.size then
1280 self.size = #self.vallist
1283 AbstractValue.render(self, ...)
1286 function MultiValue.value(self, key, val)
1287 if luci.util.contains(self.keylist, key) then
1292 table.insert(self.keylist, tostring(key))
1293 table.insert(self.vallist, tostring(val))
1296 function MultiValue.valuelist(self, section)
1297 local val = self:cfgvalue(section)
1299 if not(type(val) == "string") then
1303 return luci.util.split(val, self.delimiter)
1306 function MultiValue.validate(self, val)
1307 val = (type(val) == "table") and val or {val}
1311 for i, value in ipairs(val) do
1312 if luci.util.contains(self.keylist, value) then
1313 result = result and (result .. self.delimiter .. value) or value
1321 StaticList = class(MultiValue)
1323 function StaticList.__init__(self, ...)
1324 MultiValue.__init__(self, ...)
1326 self.valuelist = self.cfgvalue
1328 if not self.override_scheme
1329 and self.map:get_scheme(self.section.sectiontype, self.option) then
1330 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1331 if self.value and vs.values and not self.override_values then
1332 for k, v in pairs(vs.values) do
1339 function StaticList.validate(self, value)
1340 value = (type(value) == "table") and value or {value}
1343 for i, v in ipairs(value) do
1344 if luci.util.contains(self.vallist, v) then
1345 table.insert(valid, v)
1352 DynamicList = class(AbstractValue)
1354 function DynamicList.__init__(self, ...)
1355 AbstractValue.__init__(self, ...)
1356 self.template = "cbi/dynlist"
1362 function DynamicList.value(self, key, val)
1364 table.insert(self.keylist, tostring(key))
1365 table.insert(self.vallist, tostring(val))
1368 function DynamicList.formvalue(self, section)
1369 local value = AbstractValue.formvalue(self, section)
1370 value = (type(value) == "table") and value or {value}
1373 for i, v in ipairs(value) do
1375 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1376 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1377 table.insert(valid, v)
1386 TextValue - A multi-line value
1389 TextValue = class(AbstractValue)
1391 function TextValue.__init__(self, ...)
1392 AbstractValue.__init__(self, ...)
1393 self.template = "cbi/tvalue"
1399 Button = class(AbstractValue)
1401 function Button.__init__(self, ...)
1402 AbstractValue.__init__(self, ...)
1403 self.template = "cbi/button"
1404 self.inputstyle = nil
1409 FileUpload = class(AbstractValue)
1411 function FileUpload.__init__(self, ...)
1412 AbstractValue.__init__(self, ...)
1413 self.template = "cbi/upload"
1414 if not self.map.upload_fields then
1415 self.map.upload_fields = { self }
1417 self.map.upload_fields[#self.map.upload_fields+1] = self
1421 function FileUpload.formcreated(self, section)
1422 return AbstractValue.formcreated(self, section) or
1423 luci.http.formvalue("cbi.rlf."..section.."."..self.option) or
1424 luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1427 function FileUpload.cfgvalue(self, section)
1428 local val = AbstractValue.cfgvalue(self, section)
1429 if val and luci.fs.access(val) then
1435 function FileUpload.formvalue(self, section)
1436 local val = AbstractValue.formvalue(self, section)
1438 if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
1439 not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1449 function FileUpload.remove(self, section)
1450 local val = AbstractValue.formvalue(self, section)
1451 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1452 return AbstractValue.remove(self, section)
1456 FileBrowser = class(AbstractValue)
1458 function FileBrowser.__init__(self, ...)
1459 AbstractValue.__init__(self, ...)
1460 self.template = "cbi/browser"